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 SkyheimCompat v1.1.29
Cjayride.SkyheimCompat.dll
Decompiled 17 hours agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using SimpleJSON; using UnityEngine; using UnityEngine.UI; using skyheim; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("Cjayride.SkyheimCompat")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.29.0")] [assembly: AssemblyInformationalVersion("1.1.29+b66b04344934edda34df526a79d5b552b71ea3b9")] [assembly: AssemblyProduct("Cjayride.SkyheimCompat")] [assembly: AssemblyTitle("Cjayride.SkyheimCompat")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.29.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Cjayride.SkyheimCompat { [HarmonyPatch(typeof(SkyheimAltarPanel), "UpdateGui")] internal static class AltarCostHint { [HarmonyPatch(typeof(SkyheimAltar), "RPC_StopProcess")] private static class WatchFinish { private static void Postfix(SkyheimAltar __instance, bool completed) { if (completed && Object.op_Implicit((Object)(object)__instance)) { _finishedId = ((Object)__instance).GetInstanceID(); _finishedAt = Time.unscaledTime; } } } private static readonly FieldInfo AltarField = AccessTools.Field(typeof(SkyheimAltarPanel), "_altar"); private static readonly FieldInfo TimeField = AccessTools.Field(typeof(SkyheimAltarPanel), "_timeText"); private static readonly PropertyInfo TuningProperty = AccessTools.Property(typeof(SkyheimAltar), "Tuning"); private const string ImbuedKey = "sh_mod_eitr"; private static FieldInfo SenealSub; private static FieldInfo SenealCurrent; private static PropertyInfo SenealText; private static Container _container; private static SkyheimAltar _altar; private static uint _revision = uint.MaxValue; private static int _second = int.MinValue; private static bool _finishedShown; private static string _line; private static int _finishedId; private static float _finishedAt = -999f; private static string _shardPrefab; internal static void ApplySeneal(Harmony harmony) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown if (Plugin.SenealUiLoaded()) { Type type = AccessTools.TypeByName("SeneaLUI.Inv.ContainerView"); MethodInfo methodInfo = AccessTools.Method(type, "Tick", (Type[])null, (Type[])null); FieldInfo fieldInfo = AccessTools.Field(type, "_sub"); FieldInfo fieldInfo2 = AccessTools.Field(AccessTools.TypeByName("SeneaLUI.Inv.Slots"), "Current"); if (methodInfo == null || fieldInfo == null || fieldInfo2 == null) { Plugin.LogWarning("SeneaL chest window was not found. Altar shard text stays on Skyheim's own panel."); return; } SenealSub = fieldInfo; SenealCurrent = fieldInfo2; harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(AltarCostHint), "SenealTick", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log("Rune Altar requirements show on the SeneaL chest subtitle."); } } private static void SenealTick(object __instance) { object? obj = SenealCurrent?.GetValue(null); Container val = (Container)((obj is Container) ? obj : null); if (val == null) { _container = null; return; } if ((Object)(object)val != (Object)(object)_container) { _container = val; _altar = ((Component)val).GetComponent<SkyheimAltar>(); if (!Object.op_Implicit((Object)(object)_altar)) { _altar = ((Component)val).GetComponentInParent<SkyheimAltar>(); } _revision = uint.MaxValue; } if (!Object.op_Implicit((Object)(object)_altar)) { return; } int num = (_altar.Processing ? Mathf.CeilToInt(_altar.TimeRemaining) : 0); bool flag = JustFinished(_altar); uint lastRevision = val.m_lastRevision; if (lastRevision == _revision && num == _second && flag == _finishedShown) { return; } _revision = lastRevision; _second = num; _finishedShown = flag; string text = SenealStatus(_altar); if (!(text == _line)) { _line = text; if (SenealText == null) { object obj2 = SenealSub?.GetValue(__instance); SenealText = obj2?.GetType().GetProperty("text"); SenealText?.SetValue(obj2, text, null); } else { object obj3 = SenealSub?.GetValue(__instance); SenealText.SetValue(obj3, text, null); } } } private static string SenealStatus(SkyheimAltar altar) { if ((Object)(object)altar != (Object)null && altar.Processing) { return WaitText(altar.TimeRemaining) + " left"; } SkyheimArmorRecipe val = (((Object)(object)altar != (Object)null) ? altar.GetActiveRecipe() : null); if (val == null) { if (!HasImbuedArmor(altar)) { return "Put armor in"; } if (!JustFinished(altar)) { return "Item already imbued"; } return "Finished adding Eitr"; } int num = CountShards(altar); string text = ShortWait(val.CraftingTime); if (num >= val.EitrShardsRequired) { return "Close chest, " + text; } return "Need " + val.EitrShardsRequired + ", have " + num + ", " + text; } internal static string Status(SkyheimAltar altar) { if ((Object)(object)altar != (Object)null && altar.Processing) { return "Imbuing. " + WaitText(altar.TimeRemaining) + " left."; } SkyheimArmorRecipe val = (((Object)(object)altar != (Object)null) ? altar.GetActiveRecipe() : null); if (val == null) { if (!HasImbuedArmor(altar)) { return "Put a supported armor piece in. The shard cost shows here."; } if (!JustFinished(altar)) { return "Item already imbued."; } return "Finished adding Eitr."; } int num = CountShards(altar); string text = WaitText(val.CraftingTime); if (num >= val.EitrShardsRequired) { return "Close the chest to start. Takes " + text + "."; } return $"Need {val.EitrShardsRequired} Eitr Shards. You have {num}. Takes {text}."; } private static void Postfix(SkyheimAltarPanel __instance) { object? obj = AltarField?.GetValue(__instance); SkyheimAltar val = (SkyheimAltar)((obj is SkyheimAltar) ? obj : null); if (val != null && !val.Processing) { object? obj2 = TimeField?.GetValue(__instance); Text val2 = (Text)((obj2 is Text) ? obj2 : null); if (val2 != null) { val2.horizontalOverflow = (HorizontalWrapMode)0; val2.verticalOverflow = (VerticalWrapMode)1; val2.text = Status(val); } } } private static bool JustFinished(SkyheimAltar altar) { if (Object.op_Implicit((Object)(object)altar) && ((Object)altar).GetInstanceID() == _finishedId) { return Time.unscaledTime - _finishedAt < 15f; } return false; } private static bool HasImbuedArmor(SkyheimAltar altar) { Inventory val = (((Object)(object)altar != (Object)null && Object.op_Implicit((Object)(object)altar.Container)) ? ((Container)altar.Container).GetInventory() : null); if (val == null) { return false; } foreach (ItemData allItem in val.GetAllItems()) { if (allItem?.m_customData != null && allItem.m_customData.ContainsKey("sh_mod_eitr")) { return true; } } return false; } private static int CountShards(SkyheimAltar altar) { if (_shardPrefab == null) { object? obj = TuningProperty?.GetValue(altar); SkyheimAltarTuning val = (SkyheimAltarTuning)((obj is SkyheimAltarTuning) ? obj : null); _shardPrefab = (((Object)(object)val != (Object)null) ? val.EitrShardPrefab : ""); } SkyheimAltarContainer container = altar.Container; Inventory val2 = ((container != null) ? ((Container)container).GetInventory() : null); if (val2 == null || _shardPrefab.Length == 0) { return 0; } int num = 0; foreach (ItemData allItem in val2.GetAllItems()) { if ((Object)(object)allItem?.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name == _shardPrefab) { num += allItem.m_stack; } } return num; } private static string ShortWait(float seconds) { int num = Mathf.Max(0, Mathf.RoundToInt(seconds)); if (num >= 60 && num % 60 == 0) { return num / 60 + " min"; } if (num >= 60) { return num / 60 + "m " + num % 60 + "s"; } return num + "s"; } private static string WaitText(float seconds) { int num = Mathf.Max(0, Mathf.RoundToInt(seconds)); if (num >= 60 && num % 60 == 0) { int num2 = num / 60; if (num2 != 1) { return num2 + " minutes"; } return "1 minute"; } if (num >= 60) { return num / 60 + " min " + num % 60 + " sec"; } if (num != 1) { return num + " seconds"; } return "1 second"; } } [HarmonyPatch] internal static class AltarImbueCompat { private const string ModTypeKey = "sh_mod_type"; private const string DefaultExtraRecipes = "ArmorHorseClothChest_T1:20:20:0.2:0.25:240, ArmorHorseClothLegs_T1:15:15:0.1:0.25:240, ArmorHorseClothHelmet_T1:8:10:0.2:0.1:120, ArmorHorseClothChest_T2:25:25:0:0.1:300, ArmorHorseClothLegs_T2:18:15:0:0.1:300, ArmorHorseClothHelmet_T2:10:10:0:0.1:120, rae_OdinHorse_Helmet:6:5:0.2:0.5:60"; private static ConfigEntry<string> _extraRecipes; private static readonly PropertyInfo TuningProperty = AccessTools.Property(typeof(SkyheimAltar), "Tuning"); internal static void Bind(ConfigFile config) { _extraRecipes = config.Bind<string>("Altar imbue", "Extra Recipes", "ArmorHorseClothChest_T1:20:20:0.2:0.25:240, ArmorHorseClothLegs_T1:15:15:0.1:0.25:240, ArmorHorseClothHelmet_T1:8:10:0.2:0.1:120, ArmorHorseClothChest_T2:25:25:0:0.1:300, ArmorHorseClothLegs_T2:18:15:0:0.1:300, ArmorHorseClothHelmet_T2:10:10:0:0.1:120, rae_OdinHorse_Helmet:6:5:0.2:0.5:60", "Extra armor the Skyheim altar can eitr-imbue, comma separated. Format per entry: PrefabName:ShardsRequired:MaxEitr:EitrRegen:ArmorPenalty:Seconds (regen and armor penalty are fractions, e.g. 0.2 = 20%). Defaults cover the OdinHorse Skogarmadr (T1) and Drapmadr (T2) sets and the Horse Trophy Helmet. Entries for prefabs Skyheim already supports replace Skyheim's values."); } [HarmonyPatch(typeof(SkyheimAltarTuning), "RuntimeDeserialize")] [HarmonyPostfix] private static void RuntimeDeserialize_Postfix(SkyheimAltarTuning __instance) { if (__instance?.Recipes == null || _extraRecipes == null) { return; } int num = 0; string[] array = _extraRecipes.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { SkyheimArmorRecipe recipe = Parse(text.Trim()); if (recipe != null) { __instance.Recipes.RemoveAll((SkyheimArmorRecipe r) => string.Equals(r.SourceItemDrop, recipe.SourceItemDrop, StringComparison.Ordinal)); __instance.Recipes.Add(recipe); num++; } } Plugin.Log($"Altar imbue: {num} extra recipe(s) loaded, {__instance.Recipes.Count} total."); } private static SkyheimArmorRecipe Parse(string entry) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown if (entry.Length == 0) { return null; } string[] array = entry.Split(new char[1] { ':' }); if (array.Length != 6 || !int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || !TryFloat(array[2], out var value) || !TryFloat(array[3], out var value2) || !TryFloat(array[4], out var value3) || !TryFloat(array[5], out var value4)) { Plugin.LogWarning("Altar imbue: ignoring malformed recipe '" + entry + "' (expected Prefab:Shards:Eitr:Regen:Armor:Seconds)."); return null; } return new SkyheimArmorRecipe { SourceItemDrop = array[0].Trim(), EitrShardsRequired = result, EitrModifier = value, EitrRegenModifier = value2, ArmorModifier = value3, CraftingTime = value4 }; } private static bool TryFloat(string text, out float value) { return float.TryParse(text.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out value); } private static SkyheimAltarTuning GetTuning(SkyheimAltar altar) { object? obj = TuningProperty?.GetValue(altar); return (SkyheimAltarTuning)((obj is SkyheimAltarTuning) ? obj : null); } private static bool Matches(ItemData item, string prefabName, string sharedName) { if (item?.m_shared == null) { return false; } if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == prefabName) { return true; } if (sharedName != null) { return item.m_shared.m_name == sharedName; } return false; } private static string SharedNameOf(string prefabName) { GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefabName) : null); return (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_name; } private static ItemData FindImbuable(Inventory inventory, SkyheimArmorRecipe recipe) { string sharedName = SharedNameOf(recipe.SourceItemDrop); foreach (ItemData allItem in inventory.GetAllItems()) { if (Matches(allItem, recipe.SourceItemDrop, sharedName) && !allItem.m_customData.ContainsKey("sh_mod_type")) { return allItem; } } return null; } private static int CountShards(Inventory inventory, string shardPrefab) { int num = 0; foreach (ItemData allItem in inventory.GetAllItems()) { if ((Object)(object)allItem?.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name == shardPrefab) { num += allItem.m_stack; } } return num; } private static void RemoveShards(Inventory inventory, string shardPrefab, int amount) { foreach (ItemData item in new List<ItemData>(inventory.GetAllItems())) { if (amount <= 0) { break; } if (!((Object)(object)item?.m_dropPrefab == (Object)null) && !(((Object)item.m_dropPrefab).name != shardPrefab)) { int num = Mathf.Min(item.m_stack, amount); inventory.RemoveItem(item, num); amount -= num; } } } [HarmonyPatch(typeof(SkyheimAltar), "GetActiveRecipe")] [HarmonyPrefix] private static bool GetActiveRecipe_Prefix(SkyheimAltar __instance, ref SkyheimArmorRecipe __result) { SkyheimAltarTuning tuning = GetTuning(__instance); SkyheimAltarContainer container = __instance.Container; Inventory val = ((container != null) ? ((Container)container).GetInventory() : null); if (tuning?.Recipes == null || val == null) { return true; } __result = null; foreach (SkyheimArmorRecipe recipe in tuning.Recipes) { if (FindImbuable(val, recipe) != null) { __result = recipe; break; } } return false; } [HarmonyPatch(typeof(SkyheimAltar), "HasEitrShards")] [HarmonyPrefix] private static bool HasEitrShards_Prefix(SkyheimAltar __instance, SkyheimArmorRecipe recipe, ref bool __result) { SkyheimAltarTuning tuning = GetTuning(__instance); SkyheimAltarContainer container = __instance.Container; Inventory val = ((container != null) ? ((Container)container).GetInventory() : null); if ((Object)(object)tuning == (Object)null || val == null || recipe == null) { return true; } __result = CountShards(val, tuning.EitrShardPrefab) >= recipe.EitrShardsRequired; return false; } [HarmonyPatch(typeof(SkyheimAltar), "CraftItem")] [HarmonyPrefix] private static bool CraftItem_Prefix(SkyheimAltar __instance) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) SkyheimAltarTuning tuning = GetTuning(__instance); SkyheimAltarContainer container = __instance.Container; Inventory val = ((container != null) ? ((Container)container).GetInventory() : null); if (tuning?.Recipes == null || val == null) { return true; } bool flag = false; foreach (SkyheimArmorRecipe recipe in tuning.Recipes) { ItemData val2 = FindImbuable(val, recipe); if (val2 != null && CountShards(val, tuning.EitrShardPrefab) >= recipe.EitrShardsRequired) { RemoveShards(val, tuning.EitrShardPrefab, recipe.EitrShardsRequired); val2.m_customData["sh_mod_type"] = ((object)(ImbueType)0/*cast due to .constrained prefix*/).ToString(); val2.m_customData["sh_mod_eitr"] = recipe.EitrModifier.ToString(); val2.m_customData["sh_mod_eitr_regen"] = recipe.EitrRegenModifier.ToString(); val2.m_customData["sh_mod_armor"] = recipe.ArmorModifier.ToString(); val2.m_customData["sh_mod_name"] = "Imbued " + val2.m_shared.m_name; flag = true; Plugin.Log($"Altar imbue: imbued {recipe.SourceItemDrop} (+{recipe.EitrModifier} eitr) using {recipe.EitrShardsRequired} shards."); } } if (flag) { ((Container)container).Save(); } return false; } } internal static class AltarPanelFix { internal static void Apply(Harmony harmony) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(InventoryGui), "UpdateContainer", (Type[])null, (Type[])null); if (methodInfo == null) { Plugin.LogWarning("InventoryGui.UpdateContainer was not found; altar panel prefix was not replaced."); return; } harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(AltarPanelFix), "UpdateContainerPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log("Replaced Skyheim altar UpdateContainer prefix with a null-safe version."); } private static bool UpdateContainerPrefix() { SkyheimAltarPanel instance = SkyheimAltarPanel.Instance; if (!Object.op_Implicit((Object)(object)instance)) { return true; } return !((Component)instance).gameObject.activeSelf; } } [HarmonyPatch(typeof(AssetManager), "LoadItems")] internal static class AssetManager_LoadItems_Guard { private const string SentinelPrefab = "bonemass_drop"; private static bool Prefix(ObjectDB db) { if ((Object)(object)db == (Object)null) { return true; } if ((Object)(object)db.GetItemPrefab("bonemass_drop") != (Object)null) { Plugin.Log("Skipped Skyheim LoadItems; ObjectDB already has bonemass_drop."); return false; } return true; } } internal static class CooldownPrefabFix { internal static void Replace() { if (SkyheimCooldown.Instance != null) { GameObject value = Build(); AccessTools.Field(typeof(SkyheimCooldown), "_cooldownPrefab")?.SetValue(SkyheimCooldown.Instance, value); Plugin.Log("Replaced Skyheim item_cooldown prefab with a runtime overlay (fixes missing SkyheimCooldownItem)."); } } private static GameObject Build() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("item_cooldown", new Type[1] { typeof(RectTransform) }); Object.DontDestroyOnLoad((Object)(object)val); val.SetActive(false); Stretch(val.GetComponent<RectTransform>()); GameObject val2 = new GameObject("swipe", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(val.transform, false); Transform transform = val2.transform; Stretch((RectTransform)(object)((transform is RectTransform) ? transform : null)); Image component = val2.GetComponent<Image>(); ((Graphic)component).color = new Color(0f, 0f, 0f, 0.55f); ((Graphic)component).raycastTarget = false; component.sprite = WhiteSprite(); component.type = (Type)3; component.fillMethod = (FillMethod)4; component.fillOrigin = 2; component.fillClockwise = false; component.fillAmount = 0f; GameObject val3 = new GameObject("text", new Type[2] { typeof(RectTransform), typeof(Text) }); val3.transform.SetParent(val.transform, false); Transform transform2 = val3.transform; Stretch((RectTransform)(object)((transform2 is RectTransform) ? transform2 : null)); Text component2 = val3.GetComponent<Text>(); component2.alignment = (TextAnchor)4; component2.fontSize = 14; ((Graphic)component2).color = Color.white; ((Graphic)component2).raycastTarget = false; component2.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); component2.text = ""; SkyheimCooldownItem obj = val.AddComponent<SkyheimCooldownItem>(); AccessTools.Field(typeof(SkyheimCooldownItem), "_text")?.SetValue(obj, component2); AccessTools.Field(typeof(SkyheimCooldownItem), "_swipe")?.SetValue(obj, component); return val; } private static void Stretch(RectTransform rt) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)rt)) { rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero; ((Transform)rt).localScale = Vector3.one; } } private static Sprite WhiteSprite() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Texture2D whiteTexture = Texture2D.whiteTexture; return Sprite.Create(whiteTexture, new Rect(0f, 0f, (float)((Texture)whiteTexture).width, (float)((Texture)whiteTexture).height), new Vector2(0.5f, 0.5f), 100f); } } [HarmonyPatch(typeof(SkyheimPieceData), "OnBeforeSerialize")] internal static class SkyheimPieceData_OnBeforeSerialize_Patch { private static readonly FieldInfo Resources = AccessTools.Field(typeof(SkyheimPieceData), "_resources"); private static bool Prefix(SkyheimPieceData __instance) { return Resources?.GetValue(__instance) != null; } } internal static class HealBalance { private const float TickInterval = 1f; private const string RuneHealNameToken = "rune_heal"; private static readonly int HealRuneStatusHash = StringExtensionMethods.GetStableHashCode("se_rune_heal"); private static ConfigEntry<float> _totalMultiplier; private static ConfigEntry<float> _healDurationSeconds; private static ConfigEntry<float> _minIconSeconds; private static ConfigEntry<bool> _showHealVfx; private static readonly FieldInfo BaseHealth = AccessTools.Field(typeof(SE_Heal), "_baseHealth"); private static readonly FieldInfo HealthPerQuality = AccessTools.Field(typeof(SE_Heal), "_healthPerQuality"); private static readonly FieldInfo DurationPerSkill = AccessTools.Field(typeof(SE_Heal), "_durationPerSkill"); internal static void Bind(ConfigFile config) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown _totalMultiplier = config.Bind<float>("Rune healing", "Total Heal Multiplier", 0.9f, new ConfigDescription("Fraction of Skyheim's healing rune HP. 1 = 40 HP at quality 1 before Elemental Magic skill; 0.9 is about 36 HP. Each quality adds 5 HP before this multiplier.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>())); _healDurationSeconds = config.Bind<float>("Rune healing", "Heal Duration Seconds", 8f, new ConfigDescription("Spread healing over this many seconds (HoT).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); _minIconSeconds = config.Bind<float>("Rune healing", "Min Status Icon Seconds", 10f, new ConfigDescription("How long the healing status icon stays on the effect bar (allies included).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 60f), Array.Empty<object>())); _showHealVfx = config.Bind<bool>("Rune healing", "Show Heal Vfx On Allies", true, "Spawn a short heal burst VFX on each character when the healing rune HoT is applied. Its volume is the Audio slider for vfx_Potion_health_medium."); } internal static bool IsSkyheimHealRune(SE_Heal heal) { if ((Object)(object)heal == (Object)null) { return false; } if (((StatusEffect)heal).NameHash() == HealRuneStatusHash) { return true; } if ((((Object)heal).name ?? string.Empty).IndexOf("rune_heal", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } return (((StatusEffect)heal).m_name ?? string.Empty).IndexOf("sh_se_rune_heal", StringComparison.OrdinalIgnoreCase) >= 0; } internal static bool TryApplyPackHeal(SE_Heal instance, int itemLevel, float skillLevel) { if ((Object)(object)instance == (Object)null || _totalMultiplier == null) { return false; } float num = ((BaseHealth?.GetValue(instance) is float num2) ? num2 : 40f); float num3 = ((HealthPerQuality?.GetValue(instance) is float num4) ? num4 : 5f); float num5 = ((DurationPerSkill?.GetValue(instance) is float num6) ? num6 : 0.01f); int num7 = ((itemLevel < 1) ? 1 : itemLevel); float num8 = 1f + num5 * skillLevel; float num9 = (num + num3 * (float)(num7 - 1)) * num8 * _totalMultiplier.Value; if (num9 <= 0f) { return false; } float num10 = Mathf.Max(1f, _healDurationSeconds.Value); float ttl = Mathf.Max(num10, _minIconSeconds.Value); int num11 = Mathf.Max(1, Mathf.CeilToInt(num10 / 1f)); float num12 = num9 / (float)num11; ((StatusEffect)instance).m_ttl = ttl; ((SE_Stats)instance).m_healthOverTimeDuration = num10; ((SE_Stats)instance).m_healthOverTimeInterval = 1f; ((SE_Stats)instance).m_healthOverTimeTicks = num11; ((SE_Stats)instance).m_healthOverTimeTickHP = num12; ((SE_Stats)instance).m_healthOverTime = 0f; if ((Object)(object)((StatusEffect)instance).m_character != (Object)null) { HealDiagnostics.Log($"SE_Heal applied to {((StatusEffect)instance).m_character.m_name}: quality {num7}, skill {skillLevel:0.#}, total {num9:0.#} HP over {num10:0.#}s ({num11} ticks of {num12:0.##}), hp {((StatusEffect)instance).m_character.GetHealth():0}/{((StatusEffect)instance).m_character.GetMaxHealth():0}"); } return true; } internal static void TryPlayHealVfx(Character character) { //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) if (_showHealVfx == null || !_showHealVfx.Value || (Object)(object)character == (Object)null) { return; } ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab("vfx_Potion_health_medium") : null); if ((Object)(object)val == (Object)null) { ZNetScene instance2 = ZNetScene.instance; val = ((instance2 != null) ? instance2.GetPrefab("vfx_Heal") : null); } if ((Object)(object)val == (Object)null) { return; } SkyheimAudio.Enter(); try { Object.Instantiate<GameObject>(val, character.GetCenterPoint(), Quaternion.identity); } finally { SkyheimAudio.Exit(); } } } [HarmonyPatch(typeof(SE_Heal), "SetLevel")] internal static class SE_Heal_SetLevel_PackBalance_Patch { private static bool Prefix(SE_Heal __instance, int itemLevel, float skillLevel) { if ((Object)(object)__instance == (Object)null || !HealBalance.IsSkyheimHealRune(__instance)) { return true; } if (!HealBalance.TryApplyPackHeal(__instance, itemLevel, skillLevel)) { return true; } return false; } } [HarmonyPatch(typeof(SE_Heal), "Setup")] internal static class SE_Heal_Setup_Vfx_Patch { private static void Postfix(SE_Heal __instance, Character character) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)character == (Object)null) && HealBalance.IsSkyheimHealRune(__instance)) { HealBalance.TryPlayHealVfx(character); } } } internal static class HealDiagnostics { private const string HealRunePrefab = "rune_heal"; private const string HealAoePrefab = "rune_heal_aoe"; private static readonly int HealStatusHash = StringExtensionMethods.GetStableHashCode("se_rune_heal"); private static ConfigEntry<bool> _enabled; internal static bool Enabled { get { if (_enabled != null) { return _enabled.Value; } return false; } } internal static void Bind(ConfigFile config) { _enabled = config.Bind<bool>("Rune healing", "Debug Logging", false, "Log each step of a Rune of Healing cast to BepInEx/LogOutput.log. Requires a game restart to take effect."); } internal static void Log(string message) { if (Enabled) { Plugin.Log("[HealDebug] " + message); } } internal static void Apply(Harmony harmony, string configPath) { Plugin.Log("[HealDebug] Debug Logging is " + (Enabled ? "ON" : "OFF") + " (read from " + configPath + ")."); if (Enabled) { TryPatch(harmony, AccessTools.Method(typeof(Humanoid), "StartAttack", (Type[])null, (Type[])null), null, "StartAttack_Postfix"); TryPatch(harmony, AccessTools.Method(typeof(Attack), "OnAttackTrigger", (Type[])null, (Type[])null), "OnAttackTrigger_Prefix"); TryPatch(harmony, AccessTools.Method(typeof(Aoe), "Setup", (Type[])null, (Type[])null), null, "AoeSetup_Postfix"); TryPatch(harmony, AccessTools.Method(typeof(Character), "Damage", (Type[])null, (Type[])null), "Damage_Prefix", null, 800); TryPatch(harmony, AccessTools.Method(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null), "RpcDamage_Prefix", null, 800); TryPatch(harmony, AccessTools.Method(typeof(SEMan), "AddStatusEffect", new Type[5] { typeof(int), typeof(bool), typeof(int), typeof(float), typeof(short) }, (Type[])null), null, "AddStatusEffect_Postfix"); } } private static void TryPatch(Harmony harmony, MethodInfo target, string prefix = null, string postfix = null, int priority = 400) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (target == null) { Plugin.LogWarning("[HealDebug] Could not find a target for " + (prefix ?? postfix) + "; skipped."); return; } try { harmony.Patch((MethodBase)target, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(HealDiagnostics), prefix, (Type[])null) { priority = priority }, (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(HealDiagnostics), postfix, (Type[])null) { priority = priority }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { Plugin.LogWarning("[HealDebug] Failed to patch " + target.DeclaringType?.Name + "." + target.Name + ": " + ex.Message); } } private static bool IsHealRune(ItemData item) { if (item == null) { return false; } if ((Object)(object)item.m_dropPrefab != (Object)null) { if (((Object)item.m_dropPrefab).name.StartsWith("rune_heal", StringComparison.Ordinal)) { return !((Object)item.m_dropPrefab).name.StartsWith("rune_heal_aoe", StringComparison.Ordinal); } return false; } if (item.m_shared?.m_name != null) { return item.m_shared.m_name.IndexOf("rune_heal", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } private static void StartAttack_Postfix(Humanoid __instance, bool __result) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { ItemData currentWeapon = __instance.GetCurrentWeapon(); if (IsHealRune(currentWeapon)) { float num = ((SkyheimCooldown.Instance != null) ? SkyheimCooldown.Instance.GetCooldown("rune_heal") : (-1f)); bool flag = (Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ObjectDB.instance.GetStatusEffect(HealStatusHash) != (Object)null; Log(string.Format("StartAttack with Rune of Healing -> {0}; se_rune_heal registered {1}; eitr {2:0.#}/{3:0.#}, cost {4:0.#}, cooldown left {5:0.#}s, godMode {6}, debugFly {7}", __result ? "started" : "REFUSED", flag, Player.m_localPlayer.GetEitr(), ((Character)Player.m_localPlayer).GetMaxEitr(), currentWeapon.m_shared.m_attack.m_attackEitr, num, ((Character)__instance).InGodMode(), ((Character)__instance).IsDebugFlying())); } } } private static void OnAttackTrigger_Prefix(Attack __instance) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance?.m_character != (Object)(object)Player.m_localPlayer) && IsHealRune(__instance.m_weapon)) { Log(string.Format("Attack trigger fired (type {0}, projectile {1}).", __instance.m_attackType, ((Object)(object)__instance.m_attackProjectile != (Object)null) ? ((Object)__instance.m_attackProjectile).name : "none")); } } private static void AoeSetup_Postfix(Aoe __instance, Character owner, HitData hitData) { if (!((Object)(object)__instance == (Object)null) && ((Object)((Component)__instance).gameObject).name.StartsWith("rune_heal_aoe", StringComparison.Ordinal)) { Log(string.Format("Heal area spawned: owner {0}, hitOwner {1}, radius {2}, status hash {3} (expected {4}), hitData {5}.", ((Object)(object)owner != (Object)null) ? owner.m_name : "none", __instance.m_hitOwner, __instance.m_radius, __instance.m_statusEffectHash, HealStatusHash, (hitData != null) ? "set" : "null")); } } private static void Damage_Prefix(Character __instance, HitData hit) { if (!((Object)(object)__instance == (Object)null) && hit != null && hit.m_statusEffectHash == HealStatusHash) { Log($"Heal hit sent to {__instance.m_name} (netview valid {(Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()})."); } } private static void AddStatusEffect_Postfix(SEMan __instance, int nameHash, StatusEffect __result) { if (nameHash == HealStatusHash) { Character val = __instance?.m_character; Log("AddStatusEffect se_rune_heal on " + (((Object)(object)val != (Object)null) ? val.m_name : "?") + ": " + (((Object)(object)__result != (Object)null) ? "new effect added" : "no new effect (refreshed existing, not owner, or not registered)") + "."); } } private static void RpcDamage_Prefix(Character __instance, HitData hit) { if (!((Object)(object)__instance == (Object)null) && hit != null && hit.m_statusEffectHash == HealStatusHash) { bool flag = (Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsOwner(); Log($"Heal hit reached {__instance.m_name}: owner {flag}, debugFly {__instance.IsDebugFlying()}, dead {__instance.IsDead()}, teleporting {__instance.IsTeleporting()}, hp {__instance.GetHealth():0}/{__instance.GetMaxHealth():0}, attacker set {hit.HaveAttacker()}, pvp {__instance.IsPVPEnabled()}."); } } } [HarmonyPatch] internal static class HealSelfFallback { private const string HealAoe = "rune_heal_aoe"; private static readonly int HealStatusHash = StringExtensionMethods.GetStableHashCode("se_rune_heal"); private static Aoe _checking; private static bool _ownerHit; private static bool IsHealAoe(Aoe aoe) { if ((Object)(object)aoe != (Object)null) { return ((Object)((Component)aoe).gameObject).name.StartsWith("rune_heal_aoe", StringComparison.Ordinal); } return false; } [HarmonyPatch(typeof(Aoe), "CheckHits")] [HarmonyPrefix] private static void CheckHits_Prefix(Aoe __instance) { _checking = (IsHealAoe(__instance) ? __instance : null); _ownerHit = false; } [HarmonyPatch(typeof(Character), "Damage")] [HarmonyPrefix] [HarmonyPriority(800)] private static void Damage_Prefix(Character __instance, HitData hit) { if ((Object)(object)_checking != (Object)null && hit != null && (Object)(object)__instance == (Object)(object)_checking.m_owner && hit.m_statusEffectHash == HealStatusHash) { _ownerHit = true; } } [HarmonyPatch(typeof(Aoe), "CheckHits")] [HarmonyFinalizer] private static void CheckHits_Finalizer(Aoe __instance) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Invalid comparison between Unknown and I4 //IL_00cd: Unknown result type (might be due to invalid IL or missing references) Aoe checking = _checking; bool ownerHit = _ownerHit; _checking = null; _ownerHit = false; if ((Object)(object)checking == (Object)null || (Object)(object)checking != (Object)(object)__instance || ownerHit || !checking.m_hitOwner) { return; } Character owner = checking.m_owner; Player val = (Player)(object)((owner is Player) ? owner : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !((Character)val).IsDead() && !((Character)val).IsDebugFlying()) { int num = ((checking.m_statusEffectHash != 0) ? checking.m_statusEffectHash : HealStatusHash); if (num == HealStatusHash && !(Vector3.Distance(((Character)val).GetCenterPoint(), ((Component)checking).transform.position) > checking.m_radius + 2f)) { float num2 = (((int)checking.m_skill != 0 && (int)checking.m_skill != 999) ? ((Character)val).GetSkillLevel(checking.m_skill) : 0f); SEMan sEMan = ((Character)val).GetSEMan(); sEMan.RemoveStatusEffect(num, true); sEMan.AddStatusEffect(num, true, checking.m_level, num2, (short)(-1)); HealDiagnostics.Log($"Heal area missed the caster; applied se_rune_heal directly (quality {checking.m_level}, skill {num2:0.#})."); } } } } [HarmonyPatch] internal static class ImmolateAuraGuard { private const string AoePrefabName = "rune_immolate_aoe"; private static ConfigEntry<bool> _blockShockingCharge; private static ConfigEntry<float> _volumeScale; private static ConfigEntry<bool> _oneSoundPerTick; private static Aoe _currentAoe; private static readonly Dictionary<int, float> LastSoundTime = new Dictionary<int, float>(); internal static bool InImmolateHit => (Object)(object)_currentAoe != (Object)null; internal static void Bind(ConfigFile config) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown _blockShockingCharge = config.Bind<bool>("Epic Loot compat", "Block Shocking Charge From Immolation Aura", true, "When true, hits from the Immolation aura do not build or discharge Epic Loot Eikthyr Shocking Charge."); _volumeScale = config.Bind<float>("Rune immolation", "Aura Audio Volume Scale", 0.05f, new ConfigDescription("Unused. Immolation hit volume is the Audio slider for that sound. Left so older configs still load.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _oneSoundPerTick = config.Bind<bool>("Rune immolation", "One Hit Sound Per Tick", true, "When true, an aura tick that hits several enemies plays the explosion sound once instead of once per enemy."); } internal static void Apply(Harmony harmony) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown if (_blockShockingCharge != null && _blockShockingCharge.Value) { Type type = AccessTools.TypeByName("EpicLoot.MagicItemEffects.Shards.EikthyrShockingCharge"); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "OnDamageDealt", (Type[])null, (Type[])null) : null); if (!(methodInfo == null)) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ImmolateAuraGuard), "ShockingCharge_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Plugin.Log("Epic Loot: Immolation aura hits will not trigger Eikthyr Shocking Charge."); } } } private static bool IsImmolateAoe(Aoe aoe) { if ((Object)(object)aoe != (Object)null) { return ((Object)((Component)aoe).gameObject).name.StartsWith("rune_immolate_aoe", StringComparison.Ordinal); } return false; } [HarmonyPatch(typeof(Aoe), "OnHit")] [HarmonyPrefix] private static void OnHit_Prefix(Aoe __instance) { _currentAoe = (IsImmolateAoe(__instance) ? __instance : null); } [HarmonyPatch(typeof(Aoe), "OnHit")] [HarmonyFinalizer] private static void OnHit_Finalizer() { _currentAoe = null; } [HarmonyPatch(typeof(EffectList), "Create")] [HarmonyPostfix] private static void EffectList_Create_Postfix(EffectList __instance, GameObject[] __result) { if ((Object)(object)_currentAoe == (Object)null || __result == null || _volumeScale == null || __instance != _currentAoe.m_hitEffects || _volumeScale == null || !_oneSoundPerTick.Value || ClaimTickSound(_currentAoe)) { return; } foreach (GameObject val in __result) { if ((Object)(object)val == (Object)null) { continue; } ZSFX[] componentsInChildren = val.GetComponentsInChildren<ZSFX>(true); for (int j = 0; j < componentsInChildren.Length; j++) { AudioSource component = ((Component)componentsInChildren[j]).GetComponent<AudioSource>(); if ((Object)(object)component != (Object)null) { component.mute = true; } } } } private static bool ClaimTickSound(Aoe aoe) { int instanceID = ((Object)aoe).GetInstanceID(); float time = Time.time; float num = Mathf.Max(0.25f, aoe.m_hitInterval * 0.5f); if (LastSoundTime.TryGetValue(instanceID, out var value) && time - value < num) { return false; } if (LastSoundTime.Count > 64) { LastSoundTime.Clear(); } LastSoundTime[instanceID] = time; return true; } private static bool ShockingCharge_Prefix() { return !InImmolateHit; } } internal static class MagicSkillBalance { private static readonly SkillType[] MagicSkills; private static ConfigEntry<float> _gain; private static ConfigEntry<int> _loss; internal static void Bind(ConfigFile config) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown _gain = config.Bind<float>("Magic skills", "Skill Experience Gain Factor", 1f, new ConfigDescription("Factor for experience gained for the rune magic skills.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 5f), Array.Empty<object>())); _loss = config.Bind<int>("Magic skills", "Skill Experience Loss", 0, new ConfigDescription("How much experience to lose in the rune magic skills on death.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); } private static bool IsMagic(SkillType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown int num = (int)type; if (num >= 200) { return num <= 203; } return false; } [HarmonyPatch(typeof(Skills), "GetSkillDef")] [HarmonyPriority(0)] [HarmonyPostfix] private static void GetSkillDef_Postfix(SkillType type, SkillDef __result) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) if (__result != null && IsMagic(type) && _gain != null) { __result.m_increseStep = _gain.Value; } } [HarmonyPatch(typeof(Skills), "OnDeath")] [HarmonyPriority(800)] [HarmonyPrefix] private static void OnDeath_Prefix(Skills __instance, ref Dictionary<SkillType, Skill> __state) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) __state = new Dictionary<SkillType, Skill>(); if (__instance?.m_skillData == null || _loss == null) { return; } SkillType[] magicSkills = MagicSkills; foreach (SkillType key in magicSkills) { if (__instance.m_skillData.TryGetValue(key, out var value)) { __state[key] = value; if (_loss.Value > 0) { Skill obj = value; obj.m_level -= value.m_level * (float)_loss.Value / 100f; value.m_accumulator = 0f; } __instance.m_skillData.Remove(key); } } } [HarmonyPatch(typeof(Skills), "OnDeath")] [HarmonyFinalizer] private static void OnDeath_Finalizer(Skills __instance, ref Dictionary<SkillType, Skill> __state) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (__state == null || __instance?.m_skillData == null) { return; } foreach (KeyValuePair<SkillType, Skill> item in __state) { __instance.m_skillData[item.Key] = item.Value; } __state = null; } static MagicSkillBalance() { SkillType[] array = new SkillType[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); MagicSkills = (SkillType[])(object)array; } } [BepInPlugin("cjayride.skyheimcompat", "Cjayride Skyheim Compat", "1.1.29")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "cjayride.skyheimcompat"; public const string PluginName = "Cjayride Skyheim Compat"; public const string PluginVersion = "1.1.29"; internal const string SenealGuid = "seneaL.valheim.ui"; internal static Plugin Instance; private Harmony _harmony; private static MethodInfo _addWord; private static bool _applying; private static bool _loggedSuccess; private static bool _languageApplied; private void Awake() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown Instance = this; WarnIfUnfixedSkyheim(); _harmony = new Harmony("cjayride.skyheimcompat"); RemoveSkyheimInventoryTooltipPrefix(); RemoveSkyheimHudPrefabPatches(); UnpatchByDeclaringType(typeof(InventoryGui), "UpdateContainer", (HarmonyPatchType)1, "SkyheimAltarPanel"); WindfuryDamageGuard.RemoveOriginal(_harmony); AltarPanelFix.Apply(_harmony); CooldownPrefabFix.Replace(); MagicSkillBalance.Bind(((BaseUnityPlugin)this).Config); HealBalance.Bind(((BaseUnityPlugin)this).Config); RuneAuraRadius.Bind(((BaseUnityPlugin)this).Config); HealDiagnostics.Bind(((BaseUnityPlugin)this).Config); AltarImbueCompat.Bind(((BaseUnityPlugin)this).Config); AltarCostHint.ApplySeneal(_harmony); ImmolateAuraGuard.Bind(((BaseUnityPlugin)this).Config); SkyheimAudio.Bind(((BaseUnityPlugin)this).Config); _harmony.PatchAll(); SkyheimAudio.Apply(_harmony); ImmolateAuraGuard.Apply(_harmony); HealDiagnostics.Apply(_harmony, ((BaseUnityPlugin)this).Config.ConfigFilePath); SenealCooldownOverlay.Init(_harmony); ApplySkyheimLocalization(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Cjayride Skyheim Compat 1.1.29 loaded."); } internal static void Log(string message) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)message); } } internal static void LogWarning(string message) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)message); } } private void WarnIfUnfixedSkyheim() { try { Type type = AccessTools.TypeByName("SkyheimAltarPanel"); if (type == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"SkyheimAltarPanel was not found. Uninstall makail-Skyheim. Install cjayride-SkyheimFix (rewritten skyheim.dll) together with this Compat package. Compat cannot load runes if the original 1.3.12 DLL is present."); return; } FieldInfo fieldInfo = AccessTools.Field(type, "_leftSlot"); if (!(fieldInfo == null)) { string text = fieldInfo.FieldType?.Name; if (!(text == "InventoryElement")) { ((BaseUnityPlugin)this).Logger.LogError((object)("SkyheimAltarPanel._leftSlot is " + text + ", not InventoryElement. Uninstall makail-Skyheim. Install cjayride-SkyheimFix (rewritten skyheim.dll) together with this Compat package. Compat cannot load runes if the original 1.3.12 DLL is present.")); } } } catch (TypeLoadException ex) { ((BaseUnityPlugin)this).Logger.LogError((object)"Skyheim failed to load (InventoryGrid.Element was removed in Valheim 1.0). Uninstall makail-Skyheim. Install cjayride-SkyheimFix (rewritten skyheim.dll) together with this Compat package. Compat cannot load runes if the original 1.3.12 DLL is present."); ((BaseUnityPlugin)this).Logger.LogError((object)ex.ToString()); } catch (Exception ex2) { ((BaseUnityPlugin)this).Logger.LogError((object)"Skyheim failed to load. Uninstall makail-Skyheim. Install cjayride-SkyheimFix (rewritten skyheim.dll) together with this Compat package. Compat cannot load runes if the original 1.3.12 DLL is present."); ((BaseUnityPlugin)this).Logger.LogError((object)ex2.ToString()); } } internal static bool SenealUiLoaded() { if (Chainloader.PluginInfos != null) { return Chainloader.PluginInfos.ContainsKey("seneaL.valheim.ui"); } return false; } private static void RemoveSkyheimHudPrefabPatches() { if (SenealUiLoaded()) { UnpatchByDeclaringType(typeof(Hud), "Awake", (HarmonyPatchType)2, "SkyheimCooldown"); UnpatchByDeclaringType(typeof(InventoryGui), "Awake", (HarmonyPatchType)2, "SkyheimCooldown"); } } private static void UnpatchByDeclaringType(Type originalType, string methodName, HarmonyPatchType kind, string declaringTypeName) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) MethodInfo methodInfo = AccessTools.DeclaredMethod(originalType, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { return; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo == null) { return; } foreach (Patch item in (IEnumerable<Patch>)(((int)kind == 1) ? patchInfo.Prefixes : patchInfo.Postfixes)) { if (!(item.PatchMethod?.DeclaringType?.Name != declaringTypeName)) { new Harmony(item.owner).Unpatch((MethodBase)methodInfo, item.PatchMethod); Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)("Removed Skyheim " + declaringTypeName + " " + originalType.Name + "." + methodName + " so SeneaL UI can own the HUD.")); } } } } private static void RemoveSkyheimInventoryTooltipPrefix() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(InventoryGrid), "CreateItemTooltip", (Type[])null, (Type[])null); if (methodInfo == null) { return; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo == null) { return; } foreach (Patch prefix in patchInfo.Prefixes) { if (!(prefix.PatchMethod?.DeclaringType?.Name != "SkyheimArmor")) { new Harmony(prefix.owner).Unpatch((MethodBase)methodInfo, prefix.PatchMethod); Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogInfo((object)"Removed Skyheim CreateItemTooltip prefix so vanilla inventory tooltips run."); } } } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static bool IsBlockedByCooldown(Humanoid character, ItemData weapon) { if ((Object)(object)character == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)character != (Object)(object)Player.m_localPlayer) { return false; } if (SkyheimCooldown.Instance == null) { return false; } SkyheimItemData runeData = GetRuneData(weapon); if ((Object)(object)runeData == (Object)null || runeData.Cooldown <= 0f) { return false; } return SkyheimCooldown.Instance.GetCooldown(((Object)((Component)runeData).gameObject).name) > 0f; } internal static void ApplySkyheimLocalization(string languageName = null) { bool flag = !string.IsNullOrEmpty(languageName); if ((!flag && _languageApplied) || _applying || Localization.instance == null) { return; } _applying = true; try { JSONNode val = JSON.Parse(ReadLocalizationJson()); string text = (flag ? languageName : "English"); JSONNode val2 = val[text] ?? val["English"]; if (val2 == (object)null || val2.Count == 0) { Plugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)"Skyheim localization JSON had no usable language block."); } return; } if (_addWord == null) { _addWord = typeof(Localization).GetMethod("AddWord", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } if (_addWord == null) { Plugin instance2 = Instance; if (instance2 != null) { ((BaseUnityPlugin)instance2).Logger.LogWarning((object)"Localization.AddWord was not found."); } return; } int num = 0; foreach (KeyValuePair<string, JSONNode> item in val2.Linq) { _addWord.Invoke(Localization.instance, new object[2] { item.Key, JSONNode.op_Implicit(item.Value) }); num++; } _languageApplied = true; if (!_loggedSuccess) { _loggedSuccess = true; Plugin instance3 = Instance; if (instance3 != null) { ((BaseUnityPlugin)instance3).Logger.LogInfo((object)$"Applied {num} Skyheim localization entries ({text})."); } } } catch (Exception arg) { Plugin instance4 = Instance; if (instance4 != null) { ((BaseUnityPlugin)instance4).Logger.LogError((object)$"Skyheim localization load failed: {arg}"); } } finally { _applying = false; } } private static string ReadLocalizationJson() { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = Array.Find(executingAssembly.GetManifestResourceNames(), (string name) => name.EndsWith("skyheim.json", StringComparison.OrdinalIgnoreCase)); if (!string.IsNullOrEmpty(text)) { using (Stream stream = executingAssembly.GetManifestResourceStream(text)) { using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } } string path = Path.Combine(Path.GetDirectoryName(typeof(SkyheimPlugin).Assembly.Location) ?? string.Empty, "skyheim.json"); if (File.Exists(path)) { return File.ReadAllText(path); } throw new FileNotFoundException("Embedded skyheim.json was missing and no loose skyheim.json was found next to skyheim.dll."); } private static SkyheimItemData GetRuneData(ItemData item) { if (item == null) { return null; } GameObject val = item.m_dropPrefab; if ((Object)(object)val == (Object)null && (Object)(object)ObjectDB.instance != (Object)null && item.m_shared != null) { val = ObjectDB.instance.GetItemPrefab(item.m_shared.m_name); } if (!((Object)(object)val != (Object)null)) { return null; } return val.GetComponent<SkyheimItemData>(); } } [HarmonyPatch(typeof(SkyheimAttackRecall), "OnAttackTrigger")] internal static class SkyheimAttackRecall_OnAttackTrigger_Patch { private static bool Prefix(ref bool __result) { if ((Object)(object)Game.instance != (Object)null) { Game.instance.RequestRespawn(1.25f, true); } __result = true; return false; } } [HarmonyPatch(typeof(Localization), "SetLanguage")] internal static class Localization_SetLanguage_Patch { private static void Postfix(string language) { Plugin.ApplySkyheimLocalization(language); } } [HarmonyPatch(typeof(SkyheimItemData), "Awake")] internal static class SkyheimItemData_Awake_Patch { private static void Prefix() { Plugin.ApplySkyheimLocalization(); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPriority(800)] internal static class ObjectDB_Awake_Patch { private static void Prefix() { Plugin.ApplySkyheimLocalization(); } } [HarmonyPatch(typeof(Player), "GetCurrentCraftingStation")] internal static class Player_GetCurrentCraftingStation_Patch { private static void Postfix(ref CraftingStation __result) { if (!Object.op_Implicit((Object)(object)__result)) { __result = null; } } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] [HarmonyPriority(800)] internal static class Humanoid_StartAttack_Patch { private static bool Prefix(Humanoid __instance, ref bool __result) { if (!Plugin.IsBlockedByCooldown(__instance, __instance.GetCurrentWeapon())) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Attack), "Start")] [HarmonyPriority(800)] internal static class Attack_Start_Patch { private static bool Prefix(Humanoid character, ItemData weapon, ref bool __result) { if (!Plugin.IsBlockedByCooldown(character, weapon)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Attack), "StartWithoutAnimation")] [HarmonyPriority(800)] internal static class Attack_StartWithoutAnimation_Patch { private static bool Prefix(Humanoid character, ItemData weapon) { return !Plugin.IsBlockedByCooldown(character, weapon); } } [HarmonyPatch(typeof(InventoryGui), "Awake")] [HarmonyPriority(0)] internal static class InventoryGui_Awake_TooltipPrefab_Patch { private static void Postfix(InventoryGui __instance) { if (!Plugin.SenealUiLoaded()) { RestoreTooltipPrefabText(__instance.m_playerGrid); RestoreTooltipPrefabText(__instance.m_containerGrid); TryInjectInventoryCooldown(__instance); } } private static void TryInjectInventoryCooldown(InventoryGui gui) { try { if (!Object.op_Implicit((Object)(object)gui) || !Object.op_Implicit((Object)(object)gui.m_playerGrid) || !Object.op_Implicit((Object)(object)gui.m_playerGrid.m_elementPrefab) || SkyheimCooldown.Instance == null) { return; } object? obj = AccessTools.Field(typeof(SkyheimCooldown), "_cooldownPrefab")?.GetValue(SkyheimCooldown.Instance); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if (Object.op_Implicit((Object)(object)val)) { GameObject elementPrefab = gui.m_playerGrid.m_elementPrefab; if (!((Object)(object)elementPrefab.GetComponentInChildren<SkyheimCooldownItem>(true) != (Object)null)) { GameObject val2 = Object.Instantiate<GameObject>(elementPrefab); Object.Instantiate<GameObject>(val, val2.transform); gui.m_playerGrid.m_elementPrefab = val2; } } } catch (Exception ex) { Plugin.LogWarning("Skipped Skyheim inventory cooldown overlay: " + ex.Message); } } private static void RestoreTooltipPrefabText(InventoryGrid grid) { if (Object.op_Implicit((Object)(object)grid) && Object.op_Implicit((Object)(object)grid.m_elementPrefab)) { UITooltip component = grid.m_elementPrefab.GetComponent<UITooltip>(); if ((Object)(object)component != (Object)null && component.m_text == "") { component.m_text = null; } } } } [HarmonyPatch(typeof(InventoryGrid), "CreateItemTooltip")] internal static class InventoryGrid_CreateItemTooltip_Patch { private static bool _settingTooltip; private static bool Prefix(InventoryGrid __instance, ItemData item, UITooltip tooltip) { if (!TryGetCustomName(item, out var _)) { return true; } SetTooltip(__instance, item, tooltip); return false; } internal static void SetTooltip(InventoryGrid grid, ItemData item, UITooltip tooltip) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (_settingTooltip || (Object)(object)grid == (Object)null || item?.m_shared == null || (Object)(object)tooltip == (Object)null) { return; } string customName; string text = (TryGetCustomName(item, out customName) ? customName : item.m_shared.m_name); _settingTooltip = true; try { tooltip.Set(text, item.GetTooltip(-1), grid.m_tooltipAnchor, Vector2.zero); } finally { _settingTooltip = false; } } private static bool TryGetCustomName(ItemData item, out string customName) { customName = null; if (item?.m_customData != null && item.m_customData.TryGetValue("sh_mod_name", out customName)) { return !string.IsNullOrEmpty(customName); } return false; } } [HarmonyPatch(typeof(UITooltip), "OnHoverStart")] [HarmonyPriority(800)] internal static class UITooltip_OnHoverStart_Patch { private static void Prefix(UITooltip __instance) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) InventoryElement component = ((Component)__instance).GetComponent<InventoryElement>(); if ((Object)(object)component == (Object)null) { return; } InventoryGrid componentInParent = ((Component)component).GetComponentInParent<InventoryGrid>(); if (!((Object)(object)componentInParent == (Object)null) && componentInParent.m_inventory != null) { ItemData itemAt = componentInParent.m_inventory.GetItemAt(component.Position.x, component.Position.y); if (itemAt != null) { InventoryGrid_CreateItemTooltip_Patch.SetTooltip(componentInParent, itemAt, __instance); } } } } [HarmonyPatch] internal static class Attack_SkyheimEitrSpend_Patch { private static readonly HashSet<Attack> Prepaid = new HashSet<Attack>(); private static readonly Dictionary<Attack, float> HiddenCost = new Dictionary<Attack, float>(); [HarmonyPatch(typeof(Attack), "Start")] [HarmonyPostfix] private static void Start_Postfix(Attack __instance, ref bool __result) { if (__result && !((Object)(object)__instance?.m_character == (Object)null) && __instance.m_weapon != null && !((Object)(object)__instance.m_weapon.m_dropPrefab == (Object)null) && !((Object)(object)__instance.m_weapon.m_dropPrefab.GetComponent<SkyheimItemData>() == (Object)null)) { float attackEitr = __instance.GetAttackEitr((Character)(object)__instance.m_character, __instance.m_weapon); if (!(attackEitr <= 0f)) { ((Character)__instance.m_character).UseEitr(attackEitr); Prepaid.Add(__instance); } } } [HarmonyPatch(typeof(Attack), "Update")] [HarmonyPrefix] private static void Update_Prefix(Attack __instance) { if (Prepaid.Contains(__instance)) { if (__instance.m_wasInAttack || __instance.m_attackDone) { Prepaid.Remove(__instance); return; } HiddenCost[__instance] = __instance.m_attackEitr; __instance.m_attackEitr = 0f; } } [HarmonyPatch(typeof(Attack), "Update")] [HarmonyPostfix] private static void Update_Postfix(Attack __instance) { if (HiddenCost.TryGetValue(__instance, out var value)) { __instance.m_attackEitr = value; HiddenCost.Remove(__instance); } } } [HarmonyPatch(typeof(Attack), "HaveAmmo")] [HarmonyPriority(0)] internal static class Attack_HaveAmmo_Patch { private static void Postfix(Humanoid character, ItemData weapon, ref bool __result) { if (__result && Plugin.IsBlockedByCooldown(character, weapon)) { __result = false; } } } internal static class RuneAuraRadius { [HarmonyPatch(typeof(Aoe), "Setup")] private static class Aoe_Setup_Radius_Patch { private static void Postfix(Aoe __instance) { if (!((Object)(object)__instance == (Object)null)) { string name = ((Object)((Component)__instance).gameObject).name; if (name.StartsWith("rune_heal_aoe", StringComparison.Ordinal) && _healRadius != null) { __instance.m_radius = _healRadius.Value; } else if (name.StartsWith("rune_warmth_aoe", StringComparison.Ordinal) && _warmthRadius != null) { __instance.m_radius = _warmthRadius.Value; } } } } private const string HealAoe = "rune_heal_aoe"; private const string WarmthAoe = "rune_warmth_aoe"; private static ConfigEntry<float> _healRadius; private static ConfigEntry<float> _warmthRadius; internal static void Bind(ConfigFile config) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown _healRadius = config.Bind<float>("Rune healing", "Heal Radius", 5f, new ConfigDescription("Meters the Rune of Healing reaches around the caster. Skyheim's prefab is 5.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>())); _warmthRadius = config.Bind<float>("Rune warmth", "Warmth Radius", 30f, new ConfigDescription("Meters the Rune of Warmth reaches around the caster. Skyheim's prefab is 30, and it is an area by design.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>())); } } internal static class RuneConfig { private sealed class RuneBindings { public ConfigEntry<bool> Enabled; public ConfigEntry<float> Cooldown; public ConfigEntry<float> ManaDrain; public ConfigEntry<float> AttackEitr; public ConfigEntry<float> AttackStamina; public ConfigEntry<float> AttackRange; public ConfigEntry<float> DamageBlunt; public ConfigEntry<float> DamageSlash; public ConfigEntry<float> DamagePierce; public ConfigEntry<float> DamageFire; public ConfigEntry<float> DamageFrost; public ConfigEntry<float> DamageLightning; public ConfigEntry<float> DamagePoison; public ConfigEntry<float> DamageSpirit; public ConfigEntry<float> DamageChop; } private static readonly Dictionary<string, RuneBindings> Bindings = new Dictionary<string, RuneBindings>(StringComparer.OrdinalIgnoreCase); private static bool _bound; internal static void DiscoverAndApply() { if (AssetManager.AssetBundleObjects == null || AssetManager.AssetBundleObjects.Length == 0) { return; } ConfigFile config = ((BaseUnityPlugin)Plugin.Instance).Config; int num = 0; foreach (SkyheimItemData gameObject in AssetManager.GetGameObjects<SkyheimItemData>()) { ItemDrop component = ((Component)gameObject).GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData?.m_shared == null) { continue; } string name = ((Object)((Component)gameObject).gameObject).name; if (!Bindings.TryGetValue(name, out var value)) { value = BindRune(config, name, gameObject, component); value.Enabled.SettingChanged += delegate { ApplyAll(); }; value.Cooldown.SettingChanged += delegate { ApplyAll(); }; value.ManaDrain.SettingChanged += delegate { ApplyAll(); }; value.AttackEitr.SettingChanged += delegate { ApplyAll(); }; value.AttackStamina.SettingChanged += delegate { ApplyAll(); }; value.DamageBlunt.SettingChanged += delegate { ApplyAll(); }; value.DamageSlash.SettingChanged += delegate { ApplyAll(); }; value.DamagePierce.SettingChanged += delegate { ApplyAll(); }; value.DamageFire.SettingChanged += delegate { ApplyAll(); }; value.DamageFrost.SettingChanged += delegate { ApplyAll(); }; value.DamageLightning.SettingChanged += delegate { ApplyAll(); }; value.DamagePoison.SettingChanged += delegate { ApplyAll(); }; value.DamageSpirit.SettingChanged += delegate { ApplyAll(); }; value.DamageChop.SettingChanged += delegate { ApplyAll(); }; value.AttackRange.SettingChanged += delegate { ApplyAll(); }; Bindings[name] = value; } Apply(value, gameObject, component); num++; } if (!_bound && num > 0) { _bound = true; Plugin.Log(string.Format("Skyheim rune config ready for {0} runes -> BepInEx/config/{1}.cfg", num, "cjayride.skyheimcompat")); } } private static RuneBindings BindRune(ConfigFile config, string section, SkyheimItemData rune, ItemDrop itemDrop) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_018d: 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_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) SharedData shared = itemDrop.m_itemData.m_shared; Attack attack = shared.m_attack; DamageTypes damages = shared.m_damages; string text = ((Localization.instance != null) ? Localization.instance.Localize(shared.m_name) : section); RuneBindings runeBindings = new RuneBindings(); runeBindings.Enabled = config.Bind<bool>(section, "Enabled", true, "Show/craft " + text + " (" + section + ")."); runeBindings.Cooldown = config.Bind<float>(section, "Cooldown", rune.Cooldown, "Cooldown in seconds for " + text + "."); runeBindings.ManaDrain = config.Bind<float>(section, "EitrDrainWhileHeld", rune.ManaDrain, "Eitr drained per second while " + text + " is held."); runeBindings.AttackEitr = config.Bind<float>(section, "AttackEitr", attack?.m_attackEitr ?? 0f, "Eitr cost to cast " + text + "."); runeBindings.AttackStamina = config.Bind<float>(section, "AttackStamina", attack?.m_attackStamina ?? 0f, "Stamina cost to cast " + text + "."); runeBindings.AttackRange = config.Bind<float>(section, "AttackRange", attack?.m_attackRange ?? 0f, "Attack/projectile range for " + text + "."); runeBindings.DamageBlunt = config.Bind<float>(section, "DamageBlunt", damages.m_blunt, ""); runeBindings.DamageSlash = config.Bind<float>(section, "DamageSlash", damages.m_slash, ""); runeBindings.DamagePierce = config.Bind<float>(section, "DamagePierce", damages.m_pierce, ""); runeBindings.DamageFire = config.Bind<float>(section, "DamageFire", damages.m_fire, ""); runeBindings.DamageFrost = config.Bind<float>(section, "DamageFrost", damages.m_frost, ""); runeBindings.DamageLightning = config.Bind<float>(section, "DamageLightning", damages.m_lightning, ""); runeBindings.DamagePoison = config.Bind<float>(section, "DamagePoison", damages.m_poison, ""); runeBindings.DamageSpirit = config.Bind<float>(section, "DamageSpirit", damages.m_spirit, ""); runeBindings.DamageChop = config.Bind<float>(section, "DamageChop", damages.m_chop, ""); return runeBindings; } private static void ApplyAll() { foreach (SkyheimItemData gameObject in AssetManager.GetGameObjects<SkyheimItemData>()) { if (Bindings.TryGetValue(((Object)((Component)gameObject).gameObject).name, out var value)) { ItemDrop component = ((Component)gameObject).GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null) { Apply(value, gameObject, component); } } } } private static void Apply(RuneBindings bind, SkyheimItemData rune, ItemDrop itemDrop) { //IL_0079: 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_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) rune.Cooldown = bind.Cooldown.Value; rune.ManaDrain = bind.ManaDrain.Value; SharedData shared = itemDrop.m_itemData.m_shared; if (shared.m_attack != null) { shared.m_attack.m_attackEitr = bind.AttackEitr.Value; shared.m_attack.m_attackStamina = bind.AttackStamina.Value; shared.m_attack.m_attackRange = bind.AttackRange.Value; } DamageTypes damages = shared.m_damages; damages.m_blunt = bind.DamageBlunt.Value; damages.m_slash = bind.DamageSlash.Value; damages.m_pierce = bind.DamagePierce.Value; damages.m_fire = bind.DamageFire.Value; damages.m_frost = bind.DamageFrost.Value; damages.m_lightning = bind.DamageLightning.Value; damages.m_poison = bind.DamagePoison.Value; damages.m_spirit = bind.DamageSpirit.Value; damages.m_chop = bind.DamageChop.Value; shared.m_damages = damages; if (ObjectDB.instance?.m_recipes == null) { return; } foreach (Recipe recipe in ObjectDB.instance.m_recipes) { if ((Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item == (Object)(object)itemDrop) { recipe.m_enabled = bind.Enabled.Value; } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPriority(0)] internal static class ObjectDB_Awake_RuneConfig_Patch { private static void Postfix() { RuneConfig.DiscoverAndApply(); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyPriority(0)] internal static class ZNetScene_Awake_RuneConfig_Patch { private static void Postfix() { RuneConfig.DiscoverAndApply(); } } internal static class SenealCooldownOverlay { private static FieldInfo _hotbarSlots; private static FieldInfo _hotbarActions; private static FieldInfo _slotRoot; private static MethodInfo _actionRow; private static bool _loggedHotbar; private static bool _loggedMissing; internal static void Init(Harmony harmony) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown if (!Plugin.SenealUiLoaded()) { return; } Type type = AccessTools.TypeByName("SeneaLUI.Hud.HotbarView"); Type type2 = AccessTools.TypeByName("SeneaLUI.Components.Slot"); Type type3 = AccessTools.TypeByName("SeneaLUI.Inv.Slots"); if (type == null || type2 == null) { Plugin.LogWarning("SeneaL UI types were not found; rune cooldown overlay on the custom HUD is unavailable."); return; } _hotbarSlots = AccessTools.Field(type, "_slots"); _hotbarActions = AccessTools.Field(type, "_act"); _slotRoot = AccessTools.Field(type2, "Root"); _actionRow = AccessTools.Method(type3, "ActionRow", new Type[1] { typeof(Inventory) }, (Type[])null); MethodInfo methodInfo = AccessTools.Method(type, "Tick", (Type[])null, (Type[])null); if (methodInfo != null) { harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SenealCooldownOverlay), "HotbarTick_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } } private static void HotbarTick_Postfix(object __instance) { try { Player localPlayer = Player.m_localPlayer; Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null || __instance == null) { return; } AttachSlotArray(_hotbarSlots?.GetValue(__instance) as Array, (int i) => new Vector2i(i, 0)); int actionRow = 0; bool flag = false; if (_actionRow != null) { actionRow = (int)_actionRow.Invoke(null, new object[1] { val }); flag = true; } if (flag) { AttachSlotArray(_hotbarActions?.GetValue(__instance) as Array, (int i) => new Vector2i(i, actionRow)); } if (!_loggedHotbar) { _loggedHotbar = true; Plugin.Log("Attached Skyheim rune cooldown overlays to SeneaL hotbar and action slots."); } } catch (Exception ex) { if (!_loggedMissing) { _loggedMissing = true; Plugin.LogWarning("Could not attach Skyheim cooldown overlays to SeneaL slots: " + ex.Message); } } } private static void AttachSlotArray(Array slots, Func<int, Vector2i> locationForIndex) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (slots == null || _slotRoot == null) { return; } for (int i = 0; i < slots.Length; i++) { object value = slots.GetValue(i); if (value != null) { object? value2 = _slotRoot.GetValue(value); RectTransform val = (RectTransform)((value2 is RectTransform) ? value2 : null); if (Object.op_Implicit((Object)(object)val)) { CooldownPrefab.EnsureOn((Transform)(object)val, locationForIndex(i)); } } } } } internal static class CooldownPrefab { private static readonly FieldInfo LocationField = AccessTools.Field(typeof(SkyheimCooldownItem), "<Location>k__BackingField"); internal static Vector2i GetLocation(SkyheimCooldownItem item) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)item == (Object)null)) { return item.Location; } return new Vector2i(-1, -1); } internal static void SetLocation(SkyheimCooldownItem item, Vector2i location) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) LocationField?.SetValue(item, location); } internal static GameObject Get() { if (SkyheimCooldown.Instance == null) { return null; } object? obj = AccessTools.Field(typeof(SkyheimCooldown), "_cooldownPrefab")?.GetValue(SkyheimCooldown.Instance); return (GameObject)((obj is GameObject) ? obj : null); } internal static void EnsureOn(Transform parent, Vector2i location) { //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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)parent)) { return; } SkyheimCooldownItem componentInChildren = ((Component)parent).GetComponentInChildren<SkyheimCooldownItem>(true); if (Object.op_Implicit((Object)(object)componentInChildren)) { GameObject gameObject = ((Component)componentInChildren).gameObject; if (Object.op_Implicit((Object)(object)gameObject) && !gameObject.activeSelf) { gameObject.SetActive(true); } Vector2i location2 = GetLocation(componentInChildren); if (location2.x != location.x || location2.y != location.y) { SetLocation(componentInChildren, location); if (SkyheimCooldown.Instance != null) { SkyheimCooldown.Instance.RegisterUpdateable(componentInChildren); } } return; } GameObject val = Get(); if (!Object.op_Implicit((Object)(object)val)) { return; } GameObject val2 = Object.Instantiate<GameObject>(val, parent); ((Object)val2).name = "skyheim_cooldown"; Transform transform = val2.transform; Stretch((RectTransform)(object)((transform is RectTransform) ? transform : null)); val2.SetActive(true); SkyheimCooldownItem val3 = val2.GetComponent<SkyheimCooldownItem>() ?? val2.GetComponentInChildren<SkyheimCooldownItem>(true); if (!((Object)(object)val3 == (Object)null)) { SetLocation(val3, location); if (SkyheimCooldown.Instance != null) { SkyheimCooldown.Instance.RegisterUpdateable(val3); } } } private static void Stretch(RectTransform rt) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)rt)) { rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero; ((Transform)rt).localScale = Vector3.one; ((Transform)rt).SetAsLastSibling(); } } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] internal static class InventoryGrid_UpdateGui_Cooldown_Patch { private static void Postfix(InventoryGrid __instance) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || __instance.m_elements == null) { return; } foreach (InventoryElement element in __instance.m_elements) { if (Object.op_Implicit((Object)(object)element)) { CooldownPrefab.EnsureOn(((Component)element).transform, element.Position); } } } } [HarmonyPatch(typeof(SkyheimCooldown), "Update")] internal static class SkyheimCooldown_Update_Patch { private static readonly FieldInfo Updateables = AccessTools.Field(typeof(SkyheimCooldown), "_updateables"); private static void Prefix(SkyheimCooldown __instance) { if (!(Updateables?.GetValue(__instance) is List<SkyheimCooldownItem> list)) { return; } for (int num = list.Count - 1; num >= 0; num--) { if (!Object.op_Implicit((Object)(object)list[num])) { list.RemoveAt(num); } } } } [HarmonyPatch(typeof(SkyheimCooldownItem), "Awake")] internal static class SkyheimCooldownItem_Awake_Patch { private static bool Prefix() { return false; } } [HarmonyPatch(typeof(HotkeyBar), "Update")] internal static class HotkeyBar_Update_Cooldown_Patch { private static void Postfix(HotkeyBar __instance) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (Plugin.SenealUiLoaded() || (Object)(object)__instance == (Object)null || __instance.m_elements == null) { return; } for (int i = 0; i < __instance.m_elements.Count; i++) { GameObject go = __instance.m_elements[i].m_go; if (Object.op_Implicit((Object)(object)go)) { CooldownPrefab.EnsureOn(go.transform, new Vector2i(i, 0)); } } } } [HarmonyPatch(typeof(SkyheimCooldownItem), "SetCooldown")] internal static class SkyheimCooldownItem_SetCooldown_Patch { private static readonly FieldInfo Swipe = AccessTools.Field(typeof(SkyheimCooldownItem), "_swipe"); private static readonly FieldInfo Label = AccessTools.Field(typeof(SkyheimCooldownItem), "_text"); private static bool Prefix(SkyheimCooldownItem __instance) { if (!Object.op_Implicit((Object)(object)__instance)) { return false; } if (Swipe?.GetValue(__instance) is Image) { return Label?.GetValue(__instance) is Text; } return false; } } internal static class SkyheimAudio { [HarmonyPatch(typeof(ZSFX), "Play")] private static class ZSFX_Play_Patch { private static void Prefix(ZSFX __instance) { if (ShouldScale(__instance)) { Scale(__instance); } } } [HarmonyPatch(typeof(Attack), "Start")] [HarmonyPatch(typeof(Attack), "OnAttackTrigger")] [HarmonyPatch(typeof(Attack), "FireProjectileBurst")] [HarmonyPatch(typeof(Attack), "ProjectileAttackTriggered")] [HarmonyPatch(typeof(Attack), "DoMeleeAttack")] [HarmonyPatch(typeof(Attack), "DoAreaAttack")] private static class Attack_AudioScope_Patch { private static void Prefix(Attack __instance, ref bool __state) { __state = IsSkyheimWeapon(__instance); if (__state) { Enter(); } } private static void Finalizer(bool __state) { if (__state) { Exit(); } } } [HarmonyPatch(typeof(Aoe), "OnHit")] private static class Aoe_OnHit_AudioScope_Patch { private static void Prefix(Aoe __instance, ref bool __state) { __state = IsSkyheimObject(((Object)(object)__instance != (Object)null) ? ((Component)__instance).gameObject : null); if (__state) { Enter(); } } private static void Finalizer(bool __state) { if (__state) { Exit(); } } } [HarmonyPatch(typeof(StatusEffect), "Setup")] private static class StatusEffect_Setup_AudioScope_Patch { private static void Prefix(StatusEffect __instance, ref bool __state) { __state = IsSkyheimStatus(__instance); if (__state) { Enter(); } } private static void Finalizer(bool __state) { if (__state) { Exit(); } } } [HarmonyPatch(typeof(Projectile), "OnHit")] private static class Projectile_OnHit_AudioScope_Patch { private static void Prefix(Projectile __instance, ref bool __state) { __state = IsSkyheimObject(((Object)(object)__instance != (Object)null) ? ((Component)__instance).gameObject : null); if (__state) { Enter(); } } private static void Finalizer(bool __state) { if (__state) { Exit(); } } } private const float DefaultVolume = 0.5f; private static readonly Dictionary<string, ConfigEntry<float>> Volumes = new Dictionary<string, ConfigEntry<float>>(StringComparer.Ordinal); private static readonly HashSet<int> Scaled = new HashSet<int>(); private static readonly HashSet<int> BundleObjects = new HashSet<int>(); private static int _depth; internal static void Bind(ConfigFile config) { Discover(config); } internal static void Apply(Harmony harmony) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_0079: Expected O, but got Unknown Type[] nestedTypes = typeof(ChainAoe).GetNestedTypes(BindingFlags.NonPublic); foreach (Type type in nestedTypes) { if (type.Name.IndexOf("StaggerVisuals", StringComparison.Ordinal) >= 0) { MethodInfo method = type.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(method == null)) { harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(SkyheimAudio), "Enter", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SkyheimAudio), "Exit", (Type[])null), (HarmonyMethod)null); } } } } internal static void Enter() { _depth++; } internal static void Exit() { if (_depth > 0) { _depth--; } } internal static void ScaleObject(GameObject instance) { if (!((Object)(object)instance == (Object)null)) { ZSFX[] componentsInChildren = instance.GetComponentsInChildren<ZSFX>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Scale(componentsInChildren[i]); } } } private static void Discover(ConfigFile config) { if (AssetManager.AssetBundleObjects == null) { return; } for (int i = 0; i < AssetManager.AssetBundleObjects.Length; i++) { Object obj = AssetManager.AssetBundleObjects[i]; GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if (val != null) { RememberBundleObject(val); } } for (int j = 0; j < AssetManager.AssetBundleObjects.Length; j++) { Object obj2 = AssetManager.AssetBundleObjects[j]; GameObject val2 = (GameObject)(object)((obj2 is GameObject) ? obj2 : null); if (val2 == null) { continue; } ZSFX[] componentsInChildren = val2.GetComponentsInChildren<ZSFX>(true); for (int k = 0; k < componentsInChildren.Length; k++) { EnsureMark(componentsInChildren[k]); BindVolume(config, ((Object)((Component)componentsInChildren[k]).gameObject).name); } Component[] componentsInChildren2 = val2.GetComponentsInChildren<Component>(true); foreach (Component val3 in componentsInChildren2) { if ((Object)(object)val3 == (Object)null) { continue; } FieldInfo[] fields = ((object)val3).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int m = 0; m < fields.Length; m++) { if (!(fields[m].FieldType != typeof(EffectList))) { object? value = fields[m].GetValue(val3); EffectList val4 = (EffectList)((value is EffectList) ? value : null); if (val4 != null) { RegisterEffectList(config, val4); } } } } } BindVolume(config, "vfx_Potion_health_medium"); BindVolume(config, "vfx_Heal"); } private static void RememberBundleObject(GameObject go) { BundleObjects.Add(((Object)go).GetInstanceID()); Transform[] componentsInChildren = go.GetComponentsInChildren<Transform>(true); for (int i = 0; i < componentsInChildren.Length; i++) { BundleObjects.Add(((Object)((Component)componentsInChildren[i]).gameObject).GetInstanceID()); } } private static void RegisterEffectList(ConfigFile config, EffectList list) { if (list?.m_effectPrefabs == null) { return; } for (int i = 0; i < list.m_effectPrefabs.Length; i++) { GameObject prefab = list.m_effectPrefabs[i].m_prefab; if ((Object)(object)prefab == (Object)null) { continue; } ZSFX[] componentsInChildren = prefab.GetComponentsInChildren<ZSFX>(true); if (componentsInChildren.Length == 0) { BindVolume(config, ((Object)prefab).name); continue; } bool flag = BundleObjects.Contains(((Object)prefab).GetInstanceID()); for (int j = 0; j < componentsInChildren.Length; j++) { if (flag || BundleObjects.Contains(((Object)((Component)componentsInChildren[j]).gameObject).GetInstanceID())) { EnsureMark(componentsInChildren[j]); } BindVolume(config, ((Object)((Component)componentsInChildren[j]).gameObject).name); } } } private static void EnsureMark(ZSFX sound) { if ((Object)(object)sound != (Object)null && (Object)(object)((Component)sound).GetComponent<SkyheimSfxMark>() == (Object)null) { ((Component)sound).gameObject.AddComponent<SkyheimSfxMark>(); } } private static void BindVolume(ConfigFile config, string objectName) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown if (!string.IsNullOrEmpty(objectName) && !Volumes.ContainsKey(objectName)) { Volumes[objectName] = config.Bind<float>("Audio", objectName, 0.5f, new ConfigDescription("Volume scale for this Skyheim sound. 1 is the prefab's full volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); } } private static void Scale(ZSFX sound) { if ((Object)(object)sound == (Object)null || !Scaled.Add(((Object)sound).GetInstanceID())) { return; } if (!Volumes.TryGetValue(((Object)((Component)sound).gameObject).name, out var value) || value == null) { if ((Object)(object)Plugin.Instance != (Object)null) { BindVolume(((BaseUnityPlugin)Plugin.Instance).Config, ((Object)((Component)sound).gameObject).name); } if (!Volumes.TryGetValue(((Object)((Component)sound).gameObject).name, out value) || value == null) { return; } } float num = Mathf.Clamp01(value.Value); sound.m_minVol *= num; sound.m_maxVol *= num; AudioSource component = ((Component)sound).GetComponent<AudioSource>(); if ((Object)(object)component != (Object)null) { component.volume *= num; } } private static bool ShouldScale(ZSFX sound) { if ((Object)(object)sound == (Object)null) { return false; } if ((Object)(object)((Component)sound).GetComponent<SkyheimSfxMark>() != (Object)null) { return true; } return _depth > 0; } private static bool IsSkyheimWeapon(Attack attack) { Humanoid val = attack?.m_character; if (val == null) { return false; } return IsSkyheimObject(val.GetCurrentWeapon()?.m_dropPrefab); } private static bool IsSkyheimStatus(StatusEffect effect) { if ((Object)(object)effect == (Object)null) { return false; } return (((object)effect).GetType().Assembly.GetName().Name ?? string.Empty).IndexOf("skyheim", StringComparison.OrdinalIgnoreCase) >= 0; } private static bool IsSkyheimObject(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } string text = ((Object)go).name ?? string.Empty; if (text.StartsWith("rune_", StringComparison.OrdinalIgnoreCase) || text.StartsWith("sh_", StringComparison.OrdinalIgnoreCase) || text.IndexOf("skyheim", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } MonoBehaviour[] components = go.GetComponents<MonoBehaviour>(); foreach (MonoBehaviour val in components) { if (!((Object)(object)val == (Object)null) && (((object)val).GetType().Assembly.GetName().Name ?? string.Empty).IndexOf("skyheim", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } } internal sealed class SkyheimSfxMark : MonoBehaviour { } internal static class WindfuryDamageGuard { private static readonly FieldInfo WindfuryHash = AccessTools.Field(typeof(SkyheimPlugin), "_statusEffectWindfury"); internal static void RemoveOriginal(Harmony unused) { Unpatch(typeof(Character), "Damage", (HarmonyPatchType)2, "SkyheimPlugin"); Unpatch(typeof(HitData), "SetAttacker", (HarmonyPatchType)2, "SkyheimPlugin"); if (AccessTools.DeclaredMethod(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null) != null) { Unpatch(typeof(Character), "RPC_Damage", (HarmonyPatchType)1, "SkyheimPlugin"); } } private static void Unpatch(Type originalType, string methodName, HarmonyPatchType kind, string declaringTypeName) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) MethodInfo methodInfo = AccessTools.DeclaredMethod(originalType, methodName, (Type[])null, (Type[])null); if (methodInfo == null) { return; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo == null) { return; } foreach (Patch item in (IEnumerable<Patch>)(((int)kind == 1) ? patchInfo.Prefixes : patchInfo.Postfixes)) { if (!(item.PatchMethod?.DeclaringType?.Name != declaringTypeName)) { new Harmony(item.owner).Unpatch((MethodBase)methodInfo, item.PatchMethod); Plugin.Log("Removed Skyheim " + declaringTypeName + " " + originalType.Name + "." + methodName + " null crash."); } } } internal static int WindfuryStatusHash() { if (!(WindfuryHash == null)) { return (int)WindfuryHash.GetValue(null); } return 0; } } [HarmonyPatch(typeof(Character), "Damage")] internal static class Character_Damage_Windfury_Patch { private static void Postfix(Character __instance, HitData hit) { if (!Object.op_Implicit((Object)(object)__instance) || hit == null || !Object.op_Implicit((Object)(object)__instance.m_nview) || !__instance.m_nview.IsValid()) { return; } Character attacker = hit.GetAttacker(); if (!Object.op_Implicit((Object)(object)attacker) || (Object)(object)attacker != (Object)(object)Player.m_localPlayer) { return; } SEMan sEMan = attacker.GetSEMan(); int num = WindfuryDamageGuard.WindfuryStatusHash(); if (sEMan != null && num != 0 && sEMan.HaveStatusEffect(num)) { StatusEffect statusEffect = sEMan.GetStatusEffect(num); SE_Windfury val = (SE_Windfury)(object)((statusEffect is SE_Windfury) ? statusEffect : null); if (!((Object)(object)val == (Object)null)) { val.OnDamageDealt(__instance, hit); } } } } [HarmonyPatch(typeof(HitData), "SetAttacker")] internal static class HitData_SetAttacker_Patch { private static void Postfix(HitData __instance, Character attacker) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)attacker) || !attacker.IsPlayer()) { return; } Player val = (Player)(object)((attacker is Player) ? attacker : null); ItemData val2 = (((Object)(object)val != (Object)null) ? ((Humanoid)val).GetCurrentWeapon() : null); if (!((Object)(object)val2?.m_dropPrefab == (Object)null)) { SkyheimItemData component = val2.m_dropPrefab.GetComponent<SkyheimItemData>(); if ((Object)(object)component != (Object)null && component.Beneficial) { __instance.m_attacker = ZDOID.None; } } } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class Character_RPC_Damage_Patch { private static void Prefix(Character __instance, HitData hit) { if (Object.op_Implicit((Object)(object)__instance) && hit != null && hit.m_statusEffectHash != 0 && __instance.m_seman != null) { __instance.m_seman.RemoveStatusEffect(hit.m_statusEffectHash, true); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }