using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("HaldorExpansion")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("aveasura")] [assembly: AssemblyProduct("HaldorExpansion")] [assembly: AssemblyCopyright("Copyright © aveasura 2026")] [assembly: AssemblyTrademark("aveasura")] [assembly: ComVisible(false)] [assembly: Guid("8A5C36D6-1908-4277-91DF-24E5D1B9B7BA")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace HaldorExpansion { [BepInPlugin("aveasura.haldor.expansion", "Haldor Expansion", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class HaldorExpansionPlugin : BaseUnityPlugin { private sealed class CestusState { public Player Player; public float Charge; public float ShieldRemaining; public float ShieldMax; public float ShieldDecayPerTick; public int ShieldTicksLeft; public float CooldownUntil; public bool IsCasting; public float InputLockedUntil; public Coroutine ShieldRoutine; public Coroutine CastRoutine; public readonly List PreparedExplosionAudios = new List(); public float LastChargeGainTime; public Coroutine ChargeDecayRoutine; public float LastObservedHealth; public float LastObservedMaxHealth; public bool HasObservedHealth; public bool WasEquippedLastFrame; public bool ForceHideHudBar; public float LastSyncedShieldRemaining = -1f; public float LastSyncedShieldMax = -1f; public bool LastSyncedShieldActive; } private sealed class CestusAnimatorSpeedState { public bool Applied; public float LastBaseSpeed = 1f; public float LastAppliedSpeed = 1f; } [HarmonyPatch(typeof(CharacterAnimEvent), "CustomFixedUpdate")] private static class CestusAnimatorSpeedPatch { private static readonly FieldInfo CurrentAttackField = AccessTools.Field(typeof(Humanoid), "m_currentAttack"); private static void Postfix(Character ___m_character, Animator ___m_animator) { if ((Object)(object)___m_character == (Object)null || (Object)(object)___m_animator == (Object)null) { return; } Player val = (Player)(object)((___m_character is Player) ? ___m_character : null); if ((Object)(object)val == (Object)null) { return; } int instanceID = ((Object)___m_animator).GetInstanceID(); if (!CestusAnimatorStates.TryGetValue(instanceID, out var value)) { value = new CestusAnimatorSpeedState(); CestusAnimatorStates[instanceID] = value; } float speed = ___m_animator.speed; float num = speed; if (value.Applied && Mathf.Abs(speed - value.LastAppliedSpeed) < 0.0001f) { num = value.LastBaseSpeed; } ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon(); Attack val2 = null; if (CurrentAttackField != null) { object? value2 = CurrentAttackField.GetValue(val); val2 = (Attack)((value2 is Attack) ? value2 : null); } if (!((Character)val).InAttack() || !IsCestusWeapon(currentWeapon) || val2 == null || string.IsNullOrEmpty(val2.m_attackAnimation) || val2.m_attackAnimation.IndexOf("unarmed", StringComparison.OrdinalIgnoreCase) < 0) { value.Applied = false; value.LastBaseSpeed = speed; value.LastAppliedSpeed = speed; return; } float num2 = Mathf.Clamp(1.9f, 0.01f, 10f); float num3 = num * num2; if (Mathf.Abs(___m_animator.speed - num3) > 0.0001f) { ___m_animator.speed = num3; DebugLog("Cestus animator speed applied: " + num.ToString("0.###") + " -> " + num3.ToString("0.###")); } value.Applied = true; value.LastBaseSpeed = num; value.LastAppliedSpeed = num3; } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] private static class CestusMaxHealthPatch { private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { if (!((Object)(object)__instance == (Object)null)) { if (HasEquippedCestus(__instance)) { hp += 30f; } DebugLog($"[HaldorExpansion] Cestus HP bonus: equipped={HasEquippedCestus(__instance)}, hpBeforeBonus={hp}"); } } } [HarmonyPatch(typeof(Player), "GetBodyArmor")] private static class CestusArmorPenaltyPatch { private static void Postfix(Player __instance, ref float __result) { if (!((Object)(object)__instance == (Object)null)) { bool flag = HasEquippedCestus(__instance); if (flag) { __result = Mathf.Max(0f, __result - 15f); } DebugLog($"[HaldorExpansion] Cestus armor penalty: equipped={flag}, armorAfterPenalty={__result}"); } } } private sealed class DelayedDoomDotState { public Player Player; public float RemainingDamage; public int TicksLeft; public Coroutine Routine; } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] private static class PitKingChestMaxHealthPatch { private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { if (!((Object)(object)__instance == (Object)null)) { bool flag = HasPitKingChestEquipped(__instance); if (flag) { hp += 70f; } DebugLog($"[HaldorExpansion] Pit King chest HP bonus: equipped={flag}, hpAfterBonus={hp:0.##}"); } } } [CompilerGenerated] private sealed class d__67 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public CestusState state; public float shieldAmount; private Player 5__2; private long 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__67(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Expected O, but got Unknown bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; if (state == null) { result = false; } else { 5__2 = state.Player; 5__3 = (((Object)(object)5__2 != (Object)null) ? GetCestusStateKey(5__2) : 0); state.IsCasting = true; state.InputLockedUntil = Time.time + 1.05f; state.PreparedExplosionAudios.Clear(); <>1__state = -3; if ((Object)(object)5__2 == (Object)null || ((Character)5__2).IsDead()) { result = false; break; } 5__2.StartEmote("point", true); ClearHumanoidBlockState((Humanoid)(object)5__2); state.ShieldRemaining = shieldAmount; state.ShieldMax = shieldAmount; state.ShieldTicksLeft = 40; state.ShieldDecayPerTick = shieldAmount / 40f; SyncCestusShieldState(5__2, state, force: true); if (state.ShieldRoutine != null) { ((MonoBehaviour)Instance).StopCoroutine(state.ShieldRoutine); state.ShieldRoutine = null; } PlayCestusCastFx(5__2, state, shieldAmount); PlayCestusCastSfx(5__2); <>2__current = (object)new WaitForSeconds(1.05f); <>1__state = 1; result = true; } goto end_IL_0000; case 1: <>1__state = -3; if ((Object)(object)5__2 == (Object)null || ((Character)5__2).IsDead()) { result = false; break; } state.ShieldRoutine = ((MonoBehaviour)Instance).StartCoroutine(CestusShieldRoutine(state)); TriggerCestusNova(5__2, shieldAmount); PlayPreparedCestusExplosionAudio(state); DebugLog("[HaldorExpansion] Cestus cast resolved: " + $"shield={shieldAmount:0.##}, " + $"duration={1.05f:0.##}"); <>m__Finally1(); result = false; goto end_IL_0000; } <>m__Finally1(); end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; state.IsCasting = false; state.InputLockedUntil = 0f; state.CastRoutine = null; if (5__3 != 0L && ActiveCestusStates.TryGetValue(5__3, out var value) && value == state) { value.IsCasting = false; value.InputLockedUntil = 0f; value.CastRoutine = null; } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__63 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public CestusState state; private long 5__2; private float 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__63(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown bool result; try { int num = <>1__state; if (num != 0) { if (num == 1) { <>1__state = -3; Player player = state.Player; if ((Object)(object)player == (Object)null || ((Character)player).IsDead()) { result = false; } else { if (!(state.Charge <= 0.001f)) { if (!state.IsCasting && !(state.ShieldRemaining > 0.001f) && !(Time.time < state.CooldownUntil) && !(Time.time < state.LastChargeGainTime + 7f)) { float charge = state.Charge; float num2 = 6f * 5__3; state.Charge = Mathf.Max(0f, state.Charge - num2); DebugLog("[HaldorExpansion] Cestus charge decay: " + $"before={charge:0.##}, " + $"decay={num2:0.##}, " + $"after={state.Charge:0.##}"); if (state.Charge <= 0.001f) { state.Charge = 0f; result = false; goto IL_01dc; } } goto IL_0078; } result = false; } goto IL_01dc; } result = false; } else { <>1__state = -1; if (state != null) { 5__2 = (((Object)(object)state.Player != (Object)null) ? GetCestusStateKey(state.Player) : 0); 5__3 = Mathf.Max(0.02f, 0.2f); <>1__state = -3; goto IL_0078; } result = false; } goto end_IL_0000; IL_01dc: <>m__Finally1(); goto end_IL_0000; IL_0078: <>2__current = (object)new WaitForSeconds(5__3); <>1__state = 1; result = true; end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; state.ChargeDecayRoutine = null; if (5__2 != 0L && ActiveCestusStates.TryGetValue(5__2, out var value) && value == state) { value.ChargeDecayRoutine = null; } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__82 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public CestusState state; private float 5__2; private long 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__82(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown bool result; try { int num = <>1__state; if (num != 0) { if (num != 1) { result = false; } else { <>1__state = -3; Player player = state.Player; if ((Object)(object)player == (Object)null || ((Character)player).IsDead()) { result = false; } else if (state.ShieldRemaining <= 0.001f || state.ShieldTicksLeft <= 0) { result = false; } else { float num2 = Mathf.Min(state.ShieldRemaining, state.ShieldDecayPerTick); state.ShieldRemaining = Mathf.Max(0f, state.ShieldRemaining - num2); state.ShieldTicksLeft--; if (state.ShieldRemaining <= 0.001f) { ClearCestusShieldState(player, state, stopRoutine: false, syncNow: true); } else { SyncCestusShieldState(player, state); } DebugLog("[HaldorExpansion] Cestus shield decay: " + $"decay={num2:0.##}, " + $"remaining={state.ShieldRemaining:0.##}, " + $"ticksLeft={state.ShieldTicksLeft}"); if (!(state.ShieldRemaining <= 0.001f)) { goto IL_006b; } result = false; } <>m__Finally1(); } } else { <>1__state = -1; if (state != null) { 5__2 = 0.15f; 5__3 = (((Object)(object)state.Player != (Object)null) ? GetCestusStateKey(state.Player) : 0); <>1__state = -3; goto IL_006b; } result = false; } goto end_IL_0000; IL_006b: <>2__current = (object)new WaitForSeconds(5__2); <>1__state = 1; result = true; end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; state.ShieldRoutine = null; ClearCestusShieldState(state.Player, state, stopRoutine: false, syncNow: true); if (5__3 != 0L && ActiveCestusStates.TryGetValue(5__3, out var value) && value == state) { value.ShieldRoutine = null; ClearCestusShieldState(value.Player, value, stopRoutine: false, syncNow: true); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__173 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public DelayedDoomDotState state; private float 5__2; private int 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__173(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>1__state = -2; } private bool MoveNext() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown bool result; try { int num = <>1__state; if (num != 0) { if (num != 1) { result = false; } else { <>1__state = -3; Player player = state.Player; if ((Object)(object)player == (Object)null) { DebugLog("[HaldorExpansion] Delayed Doom routine stop: player null"); result = false; } else if (((Character)player).IsDead()) { DebugLog("[HaldorExpansion] Delayed Doom routine stop: player dead, " + GetCestusDebugPlayerTag(player)); result = false; } else if (state.RemainingDamage <= 0f || state.TicksLeft <= 0) { DebugLog("[HaldorExpansion] Delayed Doom routine stop: exhausted, " + $"remaining={state.RemainingDamage:0.##}, " + $"ticksLeft={state.TicksLeft}, " + GetCestusDebugPlayerTag(player)); result = false; } else { float num2 = state.RemainingDamage / (float)state.TicksLeft; num2 = Mathf.Max(0f, num2); state.RemainingDamage = Mathf.Max(0f, state.RemainingDamage - num2); state.TicksLeft--; DebugLog("[HaldorExpansion] Delayed Doom pooled tick: " + $"tick={num2:0.##}, " + $"remaining={state.RemainingDamage:0.##}, " + $"ticksLeft={state.TicksLeft}, " + GetCestusDebugPlayerTag(player)); if (!ApplyDelayedDoomHealthTick(player, num2)) { result = false; } else { if (!(state.RemainingDamage <= 0.001f)) { goto IL_006a; } result = false; } } <>m__Finally1(); } } else { <>1__state = -1; if (state != null) { 5__2 = 0.5f; 5__3 = (((Object)(object)state.Player != (Object)null) ? ((Object)state.Player).GetInstanceID() : 0); <>1__state = -3; goto IL_006a; } result = false; } goto end_IL_0000; IL_006a: <>2__current = (object)new WaitForSeconds(5__2); <>1__state = 1; result = true; end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if ((Object)(object)state.Player != (Object)null) { DebugLog("[HaldorExpansion] Delayed Doom routine finally: " + $"remainingBeforeClear={state.RemainingDamage:0.##}, " + $"ticksLeftBeforeClear={state.TicksLeft}, " + GetCestusDebugPlayerTag(state.Player)); } state.Routine = null; state.RemainingDamage = 0f; state.TicksLeft = 0; if (5__3 != 0 && ActiveDelayedDoomDots.TryGetValue(5__3, out var value) && value == state) { ActiveDelayedDoomDots.Remove(5__3); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly HashSet CestusItemKeys = new HashSet { "$item_weapon_gritcestus" }; internal const float CestusSetBonusMultiplier = 1.5f; internal static ConfigEntry CestusAbilityKeyConfig; internal static ButtonConfig CestusAbilityButton; internal const float CestusChargeMax = 100f; internal const float CestusShieldMaxHpRatio = 0.4f; internal const float CestusShieldMinFlat = 20f; internal const float CestusShieldDuration = 6f; internal const int CestusShieldTicks = 40; internal const float CestusCooldown = 15f; private const string CestusShieldZdoRemainingKey = "he_cestus_shield_remaining"; private const string CestusShieldZdoMaxKey = "he_cestus_shield_max"; private const string CestusShieldZdoActiveKey = "he_cestus_shield_active"; internal const string CestusActivationEmote = "point"; internal const float CestusCastDuration = 1.05f; internal const float CestusNovaDamageMultiplier = 2.7f; internal const float CestusNovaRadius = 4f; internal const float CestusNovaPushForce = 20f; internal const string CestusCastFxPrefabName = "vfx_StaminaUpgrade"; internal const string CestusCastSfxPrefabName = ""; internal const float CestusCastFxAudioPitch = 1.25f; internal const float CestusCastFxAudioStartOffset = 0.08f; internal const int CestusCastFxAmount = 2; internal const float CestusCastFxForwardOffset = 1f; internal const float CestusCastFxUpOffset = 0.8f; internal const float CestusCastFxBaseScale = 1f; internal const float CestusCastFxMaxScale = 1.6f; internal const float CestusCastFxReferenceShield = 100f; internal const float CestusCastFxPlaybackSpeed = 2f; internal const float CestusChargePerPostArmorDamage = 0.8f; internal const float CestusChargeDecayDelay = 7f; internal const float CestusChargeDecayPerSecond = 6f; internal const float CestusChargeDecayTickInterval = 0.2f; private static readonly Dictionary ActiveCestusStates = new Dictionary(); private static Image CestusShieldOverlayImage; private static RectTransform CestusShieldOverlayRect; private static RectTransform CestusShieldReferenceRect; private static Text CestusShieldText; private static RectTransform CestusShieldTextRect; private static bool CestusShieldOverlayLoggedMissing; private static bool _adrenalineReflectionDumped; private static readonly Dictionary CestusAnimatorStates = new Dictionary(); internal const string GritCestusPrefabName = "Bone_crushers"; private const string CestusEffectNameKey = "$se_weapon_gritcestus"; private const string CestusEffectDescKey = "$se_weapon_gritcestus_desc"; internal const string GritCestusSourcePrefab = "FistFenrirClaw"; internal const int GritCestusPrice = 2500; private const string CestusItemKey = "$item_weapon_gritcestus"; private const string CestusItemDescKey = "$item_weapon_gritcestus_desc"; private const float GritCestusWeight = 5f; private const int GritCestusMaxQuality = 1; private const float GritCestusMaxDurability = 1800f; private const float GritCestusDurabilityPerLevel = 0f; private const float GritCestusBluntDamage = 24f; private const float GritCestusLightningDamage = 4f; internal const float GritCestusBonusMaxHealth = 30f; private const float GritCestusPrimaryStaggerMultiplier = 1.35f; private const float GritCestusSecondaryStaggerMultiplier = 2.2f; private const float GritCestusAttackSpeedMultiplier = 1.9f; private const float GritCestusArmorPenalty = 15f; private const float GritCestusBlockPower = 2f; private const float GritCestusBlockPowerPerLevel = 0f; private const float GritCestusDeflectionForce = 15f; private const float GritCestusDeflectionForcePerLevel = 0f; private const float GritCestusTimedBlockBonus = 2f; internal static GameObject GritCestusPrefab; internal static ItemDrop GritCestusItemDrop; private const string ModGuid = "aveasura.haldor.expansion"; private const string ModName = "Haldor Expansion"; private const string ModVersion = "1.0.0"; internal static ManualLogSource Log; internal static HaldorExpansionPlugin Instance; private static readonly bool DebugLogging = false; internal const string DelayedDoomChestPrefabName = "ArmorDelayedDoomChest"; internal const string DelayedDoomChestSourcePrefab = "ArmorPaddedCuirass"; internal const int DelayedDoomChestPrice = 4000; internal const float DelayedDoomChestFoodRegenBonus = 5f; internal static GameObject DelayedDoomChestPrefab; internal static ItemDrop DelayedDoomChestItemDrop; private static readonly HashSet DelayedDoomItemKeys = new HashSet { "$item_chest_delayeddoom" }; internal const float DelayedDoomTriggerRatio = 0.5f; internal const float DelayedDoomImmediateRatio = 0.5f; internal const float DelayedDoomDebtRatio = 0.7f; internal const float DelayedDoomDuration = 6f; internal const int DelayedDoomTicks = 12; private static readonly HashSet DelayedDoomInternalDamageVictims = new HashSet(); private static readonly Dictionary ActiveDelayedDoomDots = new Dictionary(); private const string EmbeddedIconPrefix = "HaldorExpansion.Assets.Icons."; internal const string PitKingChestPrefabName = "ArmorPitKingChest"; internal const string PitKingChestSourcePrefab = "ArmorMageChest"; internal const int PitKingChestPrice = 4000; internal const float PitKingChestBonusMaxHealth = 70f; private const string PitKingChestItemKey = "$item_chest_pitking"; private const string PitKingChestItemDescKey = "$item_chest_pitking_desc"; private const string PitKingChestEffectNameKey = "$se_pitking_vigor"; private const string PitKingChestEffectDescKey = "$se_pitking_vigor_desc"; private const string SteelHeartSetNameKey = "$itemset_steelheart"; private const string SteelHeartSetDescKey = "$itemset_steelheart_desc"; private static readonly HashSet PitKingChestItemKeys = new HashSet { "$item_chest_pitking" }; internal static GameObject PitKingChestPrefab; internal static ItemDrop PitKingChestItemDrop; internal const string RingPrefabName = "BrisingamenRing"; internal const int RingPrice = 1600; internal static GameObject RingPrefab; internal static ItemDrop RingItemDrop; private static bool _ringReadyLogged; internal const string ShadowCrossbowPrefabName = "CrossbowSilentHunt"; internal const string ShadowCrossbowSourcePrefab = "CrossbowArbalest"; internal const int ShadowCrossbowPrice = 2500; internal const string ShadowCrossbowItemKey = "$item_crossbow_silenthunt"; internal const string ShadowCrossbowItemDescKey = "$item_crossbow_silenthunt_desc"; internal const float ShadowCrossbowBasePierceDamage = 210f; internal const float ShadowCrossbowMovementModifier = -0.35f; internal const float ShadowCrossbowBackstabBonus = 6f; internal static GameObject ShadowCrossbowPrefab; internal static ItemDrop ShadowCrossbowItemDrop; private const int DefaultTrophyValue = 10; private static readonly Dictionary ExtraSellableItems = new Dictionary { { "SurtlingCore", 30 }, { "BlackCore", 80 }, { "MoltenCore", 130 } }; private static readonly Dictionary TrophyValueOverrides = new Dictionary { { "TrophyDeer", 15 }, { "TrophyBoar", 15 }, { "TrophyNeck", 25 }, { "TrophyEikthyr", 70 }, { "TrophyGreydwarf", 15 }, { "TrophySkeleton", 15 }, { "TrophyGreydwarfShaman", 20 }, { "TrophyGhost", 25 }, { "TrophyGreydwarfBrute", 20 }, { "TrophyBjorn", 50 }, { "TrophyFrostTroll", 50 }, { "TrophySkeletonPoison", 50 }, { "TrophySkeletonHildir", 80 }, { "TrophyTheElder", 100 }, { "TrophySerpent", 500 }, { "TrophyBonemawSerpent", 500 }, { "TrophyBlob", 15 }, { "TrophyDraugr", 15 }, { "TrophyLeech", 15 }, { "TrophySurtling", 15 }, { "TrophyDraugrElite", 30 }, { "TrophyWraith", 35 }, { "TrophyAbomination", 75 }, { "TrophyKvastur", 80 }, { "TrophyBonemass", 150 }, { "TrophyWolf", 15 }, { "TrophyHatchling", 15 }, { "TrophyUlv", 20 }, { "TrophyFenring", 25 }, { "TrophyCultist", 40 }, { "TrophySGolem", 70 }, { "TrophyCultist_Hildir", 80 }, { "TrophyDragonQueen", 200 }, { "TrophyDeathsquito", 15 }, { "TrophyGoblin", 15 }, { "TrophyGrowth", 15 }, { "TrophyBjornUndead", 50 }, { "TrophyLox", 35 }, { "TrophyGoblinShaman", 30 }, { "TrophyGoblinBrute", 70 }, { "TrophyGoblinBruteBrosShaman", 80 }, { "TrophyGoblinBruteBrosBrute", 80 }, { "TrophyGoblinKing", 350 }, { "TrophySeeker", 20 }, { "TrophyTick", 20 }, { "TrophyDvergr", 20 }, { "TrophyHare", 20 }, { "TrophyGjall", 70 }, { "TrophySeekerBrute", 80 }, { "TrophySeekerQueen", 500 }, { "TrophyCharredArcher", 20 }, { "TrophyVolture", 20 }, { "TrophyAsksvin", 20 }, { "TrophyCharredMage", 25 }, { "TrophyCharredMelee", 25 }, { "TrophyMorgen", 100 }, { "TrophyFallenValkyrie", 120 }, { "TrophyFader", 1000 } }; private const float WoundedBeastCapeRegenWithDelayedDoomMultiplier = 0.5f; internal const string WoundedBeastCapePrefabName = "CapeWoundedBeast"; internal const string WoundedBeastCapeSourcePrefab = "CapeWolf"; internal const int WoundedBeastCapePrice = 2500; internal const string WoundedBeastCapeItemKey = "$item_cape_woundedbeast"; private const string WoundedBeastCapeItemDescKey = "$item_cape_woundedbeast_desc"; private const string WoundedBeastCapeEffectNameKey = "$se_cape_woundedbeast"; private const string WoundedBeastCapeEffectDescKey = "$se_cape_woundedbeast_desc"; internal const float WoundedBeastCapeIncomingDamageMultiplier = 1.15f; internal const float WoundedBeastCapeDotRegenSuppressDuration = 2f; private const float WoundedBeastCapeRegenTickInterval = 1f; internal static GameObject WoundedBeastCapePrefab; internal static ItemDrop WoundedBeastCapeItemDrop; private static readonly Dictionary WoundedBeastCapeNextRegenTickTimes = new Dictionary(); private static readonly Dictionary WoundedBeastCapeRegenSuppressedUntil = new Dictionary(); private static long GetCestusStateKey(Player player) { if ((Object)(object)player == (Object)null) { return 0L; } long playerID = player.GetPlayerID(); if (playerID != 0L) { return playerID; } return ((Object)player).GetInstanceID(); } private static CestusState GetOrCreateCestusState(Player player) { long cestusStateKey = GetCestusStateKey(player); if (!ActiveCestusStates.TryGetValue(cestusStateKey, out var value)) { value = new CestusState { Player = player, LastChargeGainTime = 0f, ChargeDecayRoutine = null, LastObservedHealth = ((Character)player).GetHealth(), LastObservedMaxHealth = ((Character)player).GetMaxHealth(), HasObservedHealth = true, WasEquippedLastFrame = false, ForceHideHudBar = false, Charge = 0f, ShieldRemaining = 0f, ShieldMax = 0f, ShieldDecayPerTick = 0f, ShieldTicksLeft = 0, CooldownUntil = 0f, IsCasting = false, InputLockedUntil = 0f, ShieldRoutine = null, CastRoutine = null }; ActiveCestusStates[cestusStateKey] = value; } value.Player = player; return value; } internal static bool HasCestusEquipped(Player player) { if ((Object)(object)player == (Object)null) { return false; } if (IsCestusWeapon(((Humanoid)player).GetCurrentWeapon())) { return true; } if (IsCestusWeapon(GetPlayerHandItem(player, "m_rightItem"))) { return true; } if (IsCestusWeapon(GetPlayerHandItem(player, "m_leftItem"))) { return true; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item == null || !item.m_equipped) { continue; } if (IsCestusWeapon(item)) { return true; } if (item.m_shared != null) { string name = item.m_shared.m_name; if (!string.IsNullOrEmpty(name) && CestusItemKeys.Contains(name)) { return true; } } } return false; } internal static bool ShouldProcessCestusLocally(Player player) { if ((Object)(object)player != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null) { return (Object)(object)player == (Object)(object)Player.m_localPlayer; } return false; } internal static string GetCestusDebugPlayerTag(Player player) { if ((Object)(object)player == (Object)null) { return "player=null"; } ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon(); ItemData playerHandItem = GetPlayerHandItem(player, "m_rightItem"); ItemData playerHandItem2 = GetPlayerHandItem(player, "m_leftItem"); bool flag = (Object)(object)player == (Object)(object)Player.m_localPlayer; ZNetView playerNView = GetPlayerNView(player); bool flag2 = (Object)(object)playerNView != (Object)null && playerNView.IsValid() && playerNView.IsOwner(); return "name=" + ((Object)player).name + ", " + $"local={flag}, " + $"owner={flag2}, " + $"pid={player.GetPlayerID()}, " + $"iid={((Object)player).GetInstanceID()}, " + "current=" + GetCestusItemDebugName(currentWeapon) + ", right=" + GetCestusItemDebugName(playerHandItem) + ", left=" + GetCestusItemDebugName(playerHandItem2); } private static ZNetView GetPlayerNView(Player player) { if ((Object)(object)player == (Object)null) { return null; } object? obj = typeof(Character).GetField("m_nview", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player); return (ZNetView)((obj is ZNetView) ? obj : null); } private static bool IsPlayerNViewOwner(Player player) { ZNetView playerNView = GetPlayerNView(player); if ((Object)(object)playerNView != (Object)null && playerNView.IsValid()) { return playerNView.IsOwner(); } return false; } private static ZDO GetPlayerZdo(Player player) { ZNetView playerNView = GetPlayerNView(player); if (!((Object)(object)playerNView != (Object)null) || !playerNView.IsValid()) { return null; } return playerNView.GetZDO(); } internal static bool IsCestusShieldAuthorityRuntime(Player player) { if (!ShouldProcessCestusLocally(player)) { return false; } ZNetView playerNView = GetPlayerNView(player); if (!((Object)(object)playerNView == (Object)null) && playerNView.IsValid()) { return playerNView.IsOwner(); } return true; } internal static float GetSyncedCestusShieldRemaining(Player player) { ZDO playerZdo = GetPlayerZdo(player); if (playerZdo == null) { return 0f; } return Mathf.Max(0f, playerZdo.GetFloat("he_cestus_shield_remaining", 0f)); } internal static float GetSyncedCestusShieldMax(Player player) { ZDO playerZdo = GetPlayerZdo(player); if (playerZdo == null) { return 0f; } return Mathf.Max(0f, playerZdo.GetFloat("he_cestus_shield_max", 0f)); } internal static float GetCestusEffectiveShieldRemaining(Player player) { if ((Object)(object)player == (Object)null) { return 0f; } CestusState orCreateCestusState = GetOrCreateCestusState(player); return Mathf.Max(Mathf.Max(0f, orCreateCestusState.ShieldRemaining), GetSyncedCestusShieldRemaining(player)); } private static void SyncCestusShieldState(Player player, CestusState state, bool force = false) { if ((Object)(object)player == (Object)null || state == null || !IsPlayerNViewOwner(player)) { return; } float num = Mathf.Max(0f, state.ShieldRemaining); float num2 = Mathf.Max(0f, state.ShieldMax); bool flag = num > 0.001f; if (force || !(Mathf.Abs(state.LastSyncedShieldRemaining - num) <= 0.01f) || !(Mathf.Abs(state.LastSyncedShieldMax - num2) <= 0.01f) || state.LastSyncedShieldActive != flag) { ZDO playerZdo = GetPlayerZdo(player); if (playerZdo != null) { playerZdo.Set("he_cestus_shield_remaining", num); playerZdo.Set("he_cestus_shield_max", num2); playerZdo.Set("he_cestus_shield_active", flag); state.LastSyncedShieldRemaining = num; state.LastSyncedShieldMax = num2; state.LastSyncedShieldActive = flag; } } } private static void ClearCestusShieldState(Player player, CestusState state, bool stopRoutine, bool syncNow) { if (state != null) { if (stopRoutine && state.ShieldRoutine != null && (Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StopCoroutine(state.ShieldRoutine); state.ShieldRoutine = null; } state.ShieldRemaining = 0f; state.ShieldMax = 0f; state.ShieldDecayPerTick = 0f; state.ShieldTicksLeft = 0; if (syncNow) { SyncCestusShieldState(player, state, force: true); return; } state.LastSyncedShieldRemaining = -1f; state.LastSyncedShieldMax = -1f; state.LastSyncedShieldActive = false; } } private static ItemData GetPlayerHandItem(Player player, string fieldName) { if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(fieldName)) { return null; } object? obj = typeof(Humanoid).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player); return (ItemData)((obj is ItemData) ? obj : null); } private static string GetCestusItemDebugName(ItemData item) { if (item == null) { return "null"; } if ((Object)(object)item.m_dropPrefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)item.m_dropPrefab).name)) { return ((Object)item.m_dropPrefab).name; } if (item.m_shared != null && !string.IsNullOrWhiteSpace(item.m_shared.m_name)) { return item.m_shared.m_name; } return "unknown"; } internal static bool HasFullCestusCharge(Player player) { if ((Object)(object)player == (Object)null) { return false; } return GetOrCreateCestusState(player).Charge >= 99.999f; } internal static float GetCestusCharge(Player player) { if ((Object)(object)player == (Object)null) { return 0f; } return Mathf.Clamp(GetOrCreateCestusState(player).Charge, 0f, 100f); } internal static bool ShouldMirrorCestusToAdrenaline(Player player) { if ((Object)(object)player == (Object)null) { return false; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (!HasCestusEquipped(player) && !(orCreateCestusState.Charge > 0.001f) && !(orCreateCestusState.ShieldRemaining > 0.001f)) { return orCreateCestusState.IsCasting; } return true; } internal static bool ShouldForceHideCestusHudBar(Player player) { if ((Object)(object)player == (Object)null) { return false; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (!HasCestusEquipped(player) && orCreateCestusState.ForceHideHudBar && orCreateCestusState.Charge <= 0.001f && orCreateCestusState.ShieldRemaining <= 0.001f) { return !orCreateCestusState.IsCasting; } return false; } internal static bool IsCestusShieldActive(Player player) { return GetCestusEffectiveShieldRemaining(player) > 0.001f; } internal static bool IsCestusCasting(Player player) { if ((Object)(object)player == (Object)null) { return false; } return GetOrCreateCestusState(player).IsCasting; } internal static bool IsCestusInputLocked(Player player) { if ((Object)(object)player == (Object)null) { return false; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (orCreateCestusState.IsCasting) { return Time.time < orCreateCestusState.InputLockedUntil; } return false; } internal static void UpdateCestusLocalEquipState(Player player) { if (ShouldProcessCestusLocally(player)) { CestusState orCreateCestusState = GetOrCreateCestusState(player); bool flag = HasCestusEquipped(player); if (flag) { orCreateCestusState.ForceHideHudBar = false; } else if (orCreateCestusState.WasEquippedLastFrame) { ResetCestusState(player, orCreateCestusState, "unequipped"); orCreateCestusState.ForceHideHudBar = true; } orCreateCestusState.WasEquippedLastFrame = flag; } } private static void ResetCestusState(Player player, CestusState state, string reason) { if (state != null) { if (state.ChargeDecayRoutine != null && (Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StopCoroutine(state.ChargeDecayRoutine); } if (state.ShieldRoutine != null && (Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StopCoroutine(state.ShieldRoutine); } if (state.CastRoutine != null && (Object)(object)Instance != (Object)null) { ((MonoBehaviour)Instance).StopCoroutine(state.CastRoutine); } state.ChargeDecayRoutine = null; state.ShieldRoutine = null; state.CastRoutine = null; state.Charge = 0f; state.LastChargeGainTime = 0f; ClearCestusShieldState(player, state, stopRoutine: false, syncNow: true); state.IsCasting = false; state.InputLockedUntil = 0f; if ((Object)(object)player != (Object)null) { state.LastObservedHealth = Mathf.Max(0f, ((Character)player).GetHealth()); state.HasObservedHealth = true; } DebugLog("[HaldorExpansion] Cestus state reset: reason=" + reason + ", " + GetCestusDebugPlayerTag(player)); } } internal static void UpdateCestusLocalDamageCharge(Player player) { if (!ShouldProcessCestusLocally(player)) { return; } CestusState orCreateCestusState = GetOrCreateCestusState(player); float num = Mathf.Max(0f, ((Character)player).GetHealth()); float num2 = Mathf.Max(0f, ((Character)player).GetMaxHealth()); if (!orCreateCestusState.HasObservedHealth) { orCreateCestusState.LastObservedHealth = num; orCreateCestusState.LastObservedMaxHealth = num2; orCreateCestusState.HasObservedHealth = true; return; } float lastObservedHealth = orCreateCestusState.LastObservedHealth; float lastObservedMaxHealth = orCreateCestusState.LastObservedMaxHealth; orCreateCestusState.LastObservedHealth = num; orCreateCestusState.LastObservedMaxHealth = num2; if (!HasCestusEquipped(player)) { return; } float num3 = lastObservedHealth - num; float num4 = Mathf.Max(0f, lastObservedMaxHealth - num2); float num5 = num3 - num4; if (num5 <= 0.05f) { return; } DebugLog("[HaldorExpansion] Observed local Cestus damage: " + GetCestusDebugPlayerTag(player) + ", " + $"prevHp={lastObservedHealth:0.##}, " + $"currentHp={num:0.##}, " + $"prevMaxHp={lastObservedMaxHealth:0.##}, " + $"currentMaxHp={num2:0.##}, " + $"effectiveDelta={num5:0.##}"); if (orCreateCestusState.ShieldRemaining > 0.001f && !((Character)player).IsDead()) { float num6 = Mathf.Min(orCreateCestusState.ShieldRemaining, num5); if (num6 > 0.001f) { orCreateCestusState.ShieldRemaining = Mathf.Max(0f, orCreateCestusState.ShieldRemaining - num6); float num7 = Mathf.Min(num2, Mathf.Max(0f, num + num6)); SetCharacterHealth((Character)(object)player, num7); orCreateCestusState.LastObservedHealth = num7; if (orCreateCestusState.ShieldRemaining <= 0.001f) { ClearCestusShieldState(player, orCreateCestusState, stopRoutine: false, syncNow: true); } else { SyncCestusShieldState(player, orCreateCestusState); } DebugLog("[HaldorExpansion] Cestus shield corrected via local observer: " + $"observedDamage={num5:0.##}, " + $"absorbed={num6:0.##}, " + $"correctedHealth={num7:0.##}, " + $"remaining={orCreateCestusState.ShieldRemaining:0.##}, " + GetCestusDebugPlayerTag(player)); num5 = Mathf.Max(0f, num5 - num6); } } AddCestusCharge(player, num5); } internal static void AddCestusCharge(Player player, float postArmorDamage) { if ((Object)(object)player == (Object)null || postArmorDamage <= 0f) { return; } if (!ShouldProcessCestusLocally(player)) { DebugLog("[HaldorExpansion] Skip Cestus charge on non-local player: " + GetCestusDebugPlayerTag(player)); return; } if (!HasCestusEquipped(player)) { DebugLog("[HaldorExpansion] Skip Cestus charge because weapon not detected: " + GetCestusDebugPlayerTag(player)); return; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (!(Time.time < orCreateCestusState.CooldownUntil) && !(orCreateCestusState.ShieldRemaining > 0.001f) && !orCreateCestusState.IsCasting) { float charge = orCreateCestusState.Charge; float num = postArmorDamage * 0.8f; orCreateCestusState.Charge = Mathf.Clamp(orCreateCestusState.Charge + num, 0f, 100f); orCreateCestusState.LastChargeGainTime = Time.time; EnsureCestusChargeDecayRoutine(orCreateCestusState); DebugLog("[HaldorExpansion] Cestus charge added: " + $"damage={postArmorDamage:0.##}, " + $"added={num:0.##}, " + $"before={charge:0.##}, " + $"after={orCreateCestusState.Charge:0.##}"); } } private static void EnsureCestusChargeDecayRoutine(CestusState state) { if (state != null && !((Object)(object)Instance == (Object)null) && state.ChargeDecayRoutine == null) { state.ChargeDecayRoutine = ((MonoBehaviour)Instance).StartCoroutine(CestusChargeDecayRoutine(state)); } } [IteratorStateMachine(typeof(d__63))] private static IEnumerator CestusChargeDecayRoutine(CestusState state) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__63(0) { state = state }; } internal static float AbsorbCestusShield(Player player, float incomingDamage) { if ((Object)(object)player == (Object)null || incomingDamage <= 0f) { return 0f; } if (!IsCestusShieldAuthorityRuntime(player)) { return 0f; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (orCreateCestusState.ShieldRemaining <= 0.001f) { return 0f; } float num = Mathf.Min(orCreateCestusState.ShieldRemaining, incomingDamage); orCreateCestusState.ShieldRemaining = Mathf.Max(0f, orCreateCestusState.ShieldRemaining - num); if (orCreateCestusState.ShieldRemaining <= 0.001f) { ClearCestusShieldState(player, orCreateCestusState, stopRoutine: false, syncNow: true); } else { SyncCestusShieldState(player, orCreateCestusState); } DebugLog("[HaldorExpansion] Cestus shield absorbed: " + $"incoming={incomingDamage:0.##}, " + $"absorbed={num:0.##}, " + $"remaining={orCreateCestusState.ShieldRemaining:0.##}, " + $"syncedRemaining={GetSyncedCestusShieldRemaining(player):0.##}, " + GetCestusDebugPlayerTag(player)); return num; } internal static float CorrectCestusShieldAfterDamage(Player player, float beforeHealth, float afterHealth) { if ((Object)(object)player == (Object)null) { return 0f; } if (!IsCestusShieldAuthorityRuntime(player)) { return 0f; } if (((Character)player).IsDead()) { return 0f; } float num = beforeHealth - afterHealth; if (num <= 0.001f) { return 0f; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (orCreateCestusState.ShieldRemaining <= 0.001f) { return 0f; } float num2 = Mathf.Min(orCreateCestusState.ShieldRemaining, num); if (num2 <= 0.001f) { return 0f; } orCreateCestusState.ShieldRemaining = Mathf.Max(0f, orCreateCestusState.ShieldRemaining - num2); float num3 = Mathf.Min(GetCharacterMaxHealth((Character)(object)player), Mathf.Max(0f, afterHealth + num2)); SetCharacterHealth((Character)(object)player, num3); if (orCreateCestusState.ShieldRemaining <= 0.001f) { ClearCestusShieldState(player, orCreateCestusState, stopRoutine: false, syncNow: true); } else { SyncCestusShieldState(player, orCreateCestusState); } DebugLog("[HaldorExpansion] Cestus shield corrected post-damage: " + $"before={beforeHealth:0.##}, " + $"after={afterHealth:0.##}, " + $"absorbed={num2:0.##}, " + $"newHealth={num3:0.##}, " + $"remaining={orCreateCestusState.ShieldRemaining:0.##}, " + GetCestusDebugPlayerTag(player)); return num2; } internal static bool TryActivateCestusShield(Player player) { if ((Object)(object)Instance == (Object)null || (Object)(object)player == (Object)null) { return false; } if (!ShouldProcessCestusLocally(player)) { return false; } if (!HasCestusEquipped(player)) { return false; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (orCreateCestusState.IsCasting) { return false; } if (orCreateCestusState.ShieldRemaining > 0.001f) { return false; } if (orCreateCestusState.Charge < 99.999f) { return false; } if (Time.time < orCreateCestusState.CooldownUntil) { return false; } float characterMaxHealth = GetCharacterMaxHealth((Character)(object)player); if (characterMaxHealth <= 0f) { return false; } float num = Mathf.Max(20f, characterMaxHealth * 0.4f); bool flag = HasCestusSetBonus(player); float num2 = (flag ? 1.5f : 1f); float num3 = num * num2; orCreateCestusState.Charge = 0f; orCreateCestusState.CooldownUntil = Time.time + 15f; if (orCreateCestusState.CastRoutine != null) { ((MonoBehaviour)Instance).StopCoroutine(orCreateCestusState.CastRoutine); orCreateCestusState.CastRoutine = null; } orCreateCestusState.CastRoutine = ((MonoBehaviour)Instance).StartCoroutine(CestusCastRoutine(orCreateCestusState, num3)); DebugLog("[HaldorExpansion] Cestus cast started: " + $"baseShield={num:0.##}, " + $"shieldMultiplier={num2:0.##}, " + $"shield={num3:0.##}, " + $"setBonus={flag}, " + $"maxHealth={characterMaxHealth:0.##}, " + $"cooldownUntil={orCreateCestusState.CooldownUntil:0.##}"); return true; } [IteratorStateMachine(typeof(d__67))] private static IEnumerator CestusCastRoutine(CestusState state, float shieldAmount) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__67(0) { state = state, shieldAmount = shieldAmount }; } private static void TriggerCestusNova(Player player, float shieldAmount) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || shieldAmount <= 0f) { return; } float num = shieldAmount * 2.7f; if (num <= 0f) { return; } Vector3 cestusNovaOrigin = GetCestusNovaOrigin(player); int num2 = 0; List allCharacters = Character.GetAllCharacters(); if (allCharacters == null) { return; } foreach (Character item in allCharacters) { if (ShouldHitCestusNovaTarget(player, item, cestusNovaOrigin)) { ApplyCestusNovaDamage(player, item, cestusNovaOrigin, num); num2++; } } DebugLog("[HaldorExpansion] Cestus nova triggered: " + $"shield={shieldAmount:0.##}, " + $"damage={num:0.##}, " + $"radius={4f:0.##}, " + $"hits={num2}"); } private static bool ShouldHitCestusNovaTarget(Player player, Character target, Vector3 origin) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)target == (Object)null) { return false; } if (player == target) { return false; } if (target.IsDead() || target.IsPlayer()) { return false; } if (!BaseAI.IsEnemy((Character)(object)player, target)) { return false; } float num = 4f + Mathf.Max(0f, target.GetRadius()); return Vector3.Distance(origin, GetCestusNovaTargetPoint(target)) <= num; } private static void ApplyCestusNovaDamage(Player player, Character target, Vector3 origin, float damage) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_0057: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_0043: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)target == (Object)null) && !(damage <= 0f)) { Vector3 cestusNovaTargetPoint = GetCestusNovaTargetPoint(target); Vector3 val = cestusNovaTargetPoint - origin; if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { val = ((Component)player).transform.forward; } HitData val2 = new HitData(); val2.m_damage.m_blunt = damage; val2.m_point = cestusNovaTargetPoint; val2.m_dir = ((Vector3)(ref val)).normalized; val2.m_pushForce = 20f; val2.m_skill = (SkillType)11; TryAssignHitAttacker(val2, (Character)(object)player); target.Damage(val2); } } private static Vector3 GetCestusNovaOrigin(Player player) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return Vector3.zero; } return ((Character)player).GetCenterPoint(); } private static Vector3 GetCestusNovaTargetPoint(Character target) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)target == (Object)null) { return Vector3.zero; } return target.GetCenterPoint(); } private static void TryAssignHitAttacker(HitData hit, Character attacker) { if (hit == null || (Object)(object)attacker == (Object)null) { return; } MethodInfo method = typeof(HitData).GetMethod("SetAttacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Character) }, null); if (method != null) { method.Invoke(hit, new object[1] { attacker }); return; } FieldInfo field = typeof(HitData).GetField("m_attacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType.IsAssignableFrom(((object)attacker).GetType())) { field.SetValue(hit, attacker); } } private static void PlayCestusCastFx(Player player, CestusState state, float shieldAmount) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_009e: 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_00b2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || string.IsNullOrWhiteSpace("vfx_StaminaUpgrade")) { return; } GameObject prefab = ZNetScene.instance.GetPrefab("vfx_StaminaUpgrade"); if ((Object)(object)prefab == (Object)null) { DebugLog("[HaldorExpansion] Missing Cestus FX prefab: vfx_StaminaUpgrade"); return; } Vector3 cestusCastFxOrigin = GetCestusCastFxOrigin(player); Quaternion val = Quaternion.LookRotation(((Component)player).transform.forward, Vector3.up); float num = Mathf.Clamp01(shieldAmount / Mathf.Max(1f, 100f)); float num2 = Mathf.Lerp(1f, 1.6f, num); int num3 = Mathf.Max(1, 2); for (int i = 0; i < num3; i++) { GameObject obj = Object.Instantiate(prefab, cestusCastFxOrigin, val); Transform transform = obj.transform; transform.localScale *= num2; TrySpeedUpCestusFx(obj, state); } } private static void TrySpeedUpCestusFx(GameObject fxInstance, CestusState state) { //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)(object)fxInstance == (Object)null) { return; } Animator[] componentsInChildren = fxInstance.GetComponentsInChildren(true); foreach (Animator val in componentsInChildren) { if ((Object)(object)val != (Object)null) { val.speed *= 2f; } } ParticleSystem[] componentsInChildren2 = fxInstance.GetComponentsInChildren(true); foreach (ParticleSystem val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null)) { MainModule main = val2.main; ((MainModule)(ref main)).simulationSpeed = ((MainModule)(ref main)).simulationSpeed * 2f; } } ZSFX[] componentsInChildren3 = fxInstance.GetComponentsInChildren(true); DebugLog($"[HaldorExpansion] ZSFX found: {componentsInChildren3.Length}"); ZSFX[] array = componentsInChildren3; foreach (ZSFX val3 in array) { if ((Object)(object)val3 == (Object)null) { continue; } AudioSource cestusFxAudioSource = GetCestusFxAudioSource(val3); AudioClip[] cestusFxAudioClips = GetCestusFxAudioClips(val3); if ((Object)(object)cestusFxAudioSource == (Object)null) { continue; } string text = ((Object)((Component)val3).gameObject).name.ToLowerInvariant(); DebugLog("[HaldorExpansion] ZSFX name=" + ((Object)((Component)val3).gameObject).name + ", " + $"clips={((cestusFxAudioClips != null) ? cestusFxAudioClips.Length : 0)}, " + "clip=" + (((Object)(object)cestusFxAudioSource.clip != (Object)null) ? ((Object)cestusFxAudioSource.clip).name : "null") + ", " + $"isPlaying={cestusFxAudioSource.isPlaying}, playOnAwake={cestusFxAudioSource.playOnAwake}, " + $"pitch={cestusFxAudioSource.pitch:0.##}"); ((Behaviour)val3).enabled = false; cestusFxAudioSource.playOnAwake = false; cestusFxAudioSource.Stop(); if (cestusFxAudioClips != null && cestusFxAudioClips.Length != 0 && !((Object)(object)cestusFxAudioClips[0] == (Object)null)) { cestusFxAudioSource.clip = cestusFxAudioClips[0]; if (text.Contains("expl")) { state?.PreparedExplosionAudios.Add(cestusFxAudioSource); DebugLog("[HaldorExpansion] Prepared explosion audio: " + ((Object)((Component)val3).gameObject).name + ", clip=" + ((Object)cestusFxAudioSource.clip).name); continue; } cestusFxAudioSource.pitch *= 1.25f; float num = 0f; num = (cestusFxAudioSource.time = Mathf.Clamp(0.08f, 0f, Mathf.Max(0f, cestusFxAudioSource.clip.length - 0.01f))); cestusFxAudioSource.Play(); DebugLog("[HaldorExpansion] Manual FX sound play: name=" + ((Object)((Component)val3).gameObject).name + ", clip=" + ((Object)cestusFxAudioSource.clip).name + ", " + $"pitch={cestusFxAudioSource.pitch:0.##}, startOffset={num:0.##}"); } } } internal static bool HasCestusSetBonus(Player player) { if ((Object)(object)player != (Object)null && HasCestusEquipped(player)) { return HasPitKingChestEquipped(player); } return false; } private static void PlayPreparedCestusExplosionAudio(CestusState state) { if (state == null || state.PreparedExplosionAudios == null || state.PreparedExplosionAudios.Count == 0) { return; } foreach (AudioSource preparedExplosionAudio in state.PreparedExplosionAudios) { if (!((Object)(object)preparedExplosionAudio == (Object)null) && !((Object)(object)preparedExplosionAudio.clip == (Object)null)) { preparedExplosionAudio.Stop(); preparedExplosionAudio.time = 0f; preparedExplosionAudio.Play(); DebugLog("[HaldorExpansion] Explosion audio played: " + $"clip={((Object)preparedExplosionAudio.clip).name}, pitch={preparedExplosionAudio.pitch:0.##}"); } } state.PreparedExplosionAudios.Clear(); } private static AudioSource GetCestusFxAudioSource(ZSFX sfx) { if ((Object)(object)sfx == (Object)null) { return null; } object? obj = typeof(ZSFX).GetField("m_audioSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(sfx); return (AudioSource)((obj is AudioSource) ? obj : null); } private static AudioClip[] GetCestusFxAudioClips(ZSFX sfx) { if ((Object)(object)sfx == (Object)null) { return null; } return typeof(ZSFX).GetField("m_audioClips", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(sfx) as AudioClip[]; } private static void PlayCestusCastSfx(Player player) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null) && !string.IsNullOrWhiteSpace("")) { GameObject prefab = ZNetScene.instance.GetPrefab(""); if ((Object)(object)prefab == (Object)null) { DebugLog("[HaldorExpansion] Missing Cestus SFX prefab: "); return; } Vector3 cestusCastFxOrigin = GetCestusCastFxOrigin(player); Quaternion val = Quaternion.LookRotation(((Component)player).transform.forward, Vector3.up); Object.Instantiate(prefab, cestusCastFxOrigin, val); } } private static Vector3 GetCestusCastFxOrigin(Player player) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_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_0009: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return Vector3.zero; } return ((Character)player).GetCenterPoint() + ((Component)player).transform.forward * 1f + Vector3.up * 0.8f; } [IteratorStateMachine(typeof(d__82))] private static IEnumerator CestusShieldRoutine(CestusState state) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__82(0) { state = state }; } internal static void ClearHumanoidAttackState(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null)) { SetFieldIfExists(humanoid, "m_attack", false); SetFieldIfExists(humanoid, "m_secondaryAttack", false); SetFieldIfExists(humanoid, "m_attackHold", false); } } internal static void ClearHumanoidBlockState(Humanoid humanoid) { if (!((Object)(object)humanoid == (Object)null)) { SetFieldIfExists(humanoid, "m_blocking", false); SetFieldIfExists(humanoid, "m_blockTimer", 0f); } } internal static float GetCestusShieldNormalized(Player player) { if ((Object)(object)player == (Object)null) { return 0f; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (orCreateCestusState.ShieldRemaining <= 0.001f || orCreateCestusState.ShieldMax <= 0.001f) { return 0f; } return Mathf.Clamp01(orCreateCestusState.ShieldRemaining / orCreateCestusState.ShieldMax); } internal static float GetCestusShieldBarNormalized(Player player) { if ((Object)(object)player == (Object)null) { return 0f; } CestusState orCreateCestusState = GetOrCreateCestusState(player); if (orCreateCestusState.ShieldRemaining <= 0.001f) { return 0f; } float characterMaxHealth = GetCharacterMaxHealth((Character)(object)player); if (characterMaxHealth <= 0f) { return 0f; } return Mathf.Clamp01(orCreateCestusState.ShieldRemaining / characterMaxHealth); } internal static float GetCestusHealthNormalized(Player player) { if ((Object)(object)player == (Object)null) { return 0f; } float characterMaxHealth = GetCharacterMaxHealth((Character)(object)player); if (characterMaxHealth <= 0f) { return 0f; } return Mathf.Clamp01(((Character)player).GetHealth() / characterMaxHealth); } private void RegisterCestusInput() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown CestusAbilityKeyConfig = ((BaseUnityPlugin)this).Config.Bind("Cestus", "Ability Key", (KeyCode)325, "Key for activating the Cestus ability"); CestusAbilityButton = new ButtonConfig { Name = "CestusAbility", Config = CestusAbilityKeyConfig, HintToken = "$cestus_ability", BlockOtherInputs = true }; InputManager.Instance.AddButton("aveasura.haldor.expansion", CestusAbilityButton); } internal static void UpdateCestusShieldOverlay(Hud hud, Player player) { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hud == (Object)null) { return; } EnsureCestusShieldOverlay(hud); if ((Object)(object)CestusShieldOverlayImage == (Object)null || (Object)(object)CestusShieldOverlayRect == (Object)null || (Object)(object)CestusShieldReferenceRect == (Object)null || (Object)(object)CestusShieldText == (Object)null || (Object)(object)CestusShieldTextRect == (Object)null) { return; } if ((Object)(object)player == (Object)null || ((Character)player).IsDead()) { if (((Component)CestusShieldOverlayImage).gameObject.activeSelf) { ((Component)CestusShieldOverlayImage).gameObject.SetActive(false); } if (((Component)CestusShieldText).gameObject.activeSelf) { ((Component)CestusShieldText).gameObject.SetActive(false); } return; } CestusState orCreateCestusState = GetOrCreateCestusState(player); float cestusShieldBarNormalized = GetCestusShieldBarNormalized(player); bool flag = cestusShieldBarNormalized > 0.001f; if (((Component)CestusShieldOverlayImage).gameObject.activeSelf != flag) { ((Component)CestusShieldOverlayImage).gameObject.SetActive(flag); } if (((Component)CestusShieldText).gameObject.activeSelf != flag) { ((Component)CestusShieldText).gameObject.SetActive(flag); } if (flag) { float cestusHealthNormalized = GetCestusHealthNormalized(player); CopyRectTransform(CestusShieldReferenceRect, CestusShieldOverlayRect); Rect rect = CestusShieldReferenceRect.rect; float width = ((Rect)(ref rect)).width; Vector2 anchoredPosition = CestusShieldReferenceRect.anchoredPosition; float num = width * cestusShieldBarNormalized; float num2 = width * cestusHealthNormalized; CestusShieldOverlayRect.anchoredPosition = anchoredPosition + new Vector2(num2, 0f); CestusShieldOverlayImage.fillAmount = cestusShieldBarNormalized; ((Transform)CestusShieldTextRect).rotation = Quaternion.identity; CestusShieldText.text = Mathf.CeilToInt(orCreateCestusState.ShieldRemaining).ToString(); CestusShieldTextRect.sizeDelta = new Vector2(Mathf.Max(36f, num), 18f); CestusShieldTextRect.anchoredPosition = new Vector2(num * 0.5f, 0f); } } private static void EnsureCestusShieldOverlay(Hud hud) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)CestusShieldOverlayImage != (Object)null && (Object)(object)CestusShieldOverlayRect != (Object)null && (Object)(object)CestusShieldReferenceRect != (Object)null && (Object)(object)CestusShieldText != (Object)null && (Object)(object)CestusShieldTextRect != (Object)null) { return; } Component val = FindReferenceHealthBarComponent(hud); if ((Object)(object)val == (Object)null) { if (!CestusShieldOverlayLoggedMissing) { CestusShieldOverlayLoggedMissing = true; DebugLog("[HaldorExpansion] Cestus segmented HUD: health bar component not found"); } return; } RectTransform component = val.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)((Transform)component).parent == (Object)null) { if (!CestusShieldOverlayLoggedMissing) { CestusShieldOverlayLoggedMissing = true; DebugLog("[HaldorExpansion] Cestus segmented HUD: reference RectTransform not found"); } return; } GameObject val2 = new GameObject("HaldorExpansion_CestusShieldSegment", new Type[1] { typeof(RectTransform) }); RectTransform component2 = val2.GetComponent(); ((Transform)component2).SetParent(((Transform)component).parent, false); CopyRectTransform(component, component2); Image val3 = val2.AddComponent(); ((Graphic)val3).raycastTarget = false; val3.type = (Type)3; val3.fillMethod = (FillMethod)0; val3.fillOrigin = 0; val3.fillAmount = 0f; ((Graphic)val3).color = new Color(0.62f, 0.62f, 0.62f, 0.78f); Image val4 = val.GetComponent(); if ((Object)(object)val4 == (Object)null) { val4 = val.GetComponentInChildren(true); } if ((Object)(object)val4 != (Object)null) { val3.sprite = val4.sprite; ((Graphic)val3).material = ((Graphic)val4).material; val3.preserveAspect = val4.preserveAspect; } ((Transform)component2).SetSiblingIndex(((Transform)component).GetSiblingIndex() + 1); val2.SetActive(false); GameObject val5 = new GameObject("HaldorExpansion_CestusShieldText", new Type[1] { typeof(RectTransform) }); RectTransform component3 = val5.GetComponent(); ((Transform)component3).SetParent((Transform)(object)component2, false); component3.anchorMin = new Vector2(0f, 0.5f); component3.anchorMax = new Vector2(0f, 0.5f); component3.pivot = new Vector2(0.5f, 0.5f); component3.sizeDelta = new Vector2(64f, 18f); ((Transform)component3).localScale = Vector3.one; ((Transform)component3).rotation = Quaternion.identity; component3.anchoredPosition = Vector2.zero; Text val6 = val5.AddComponent(); ((Graphic)val6).raycastTarget = false; val6.alignment = (TextAnchor)4; val6.horizontalOverflow = (HorizontalWrapMode)1; val6.verticalOverflow = (VerticalWrapMode)1; val6.resizeTextForBestFit = false; val6.fontSize = 14; val6.fontStyle = (FontStyle)1; ((Graphic)val6).color = new Color(1f, 1f, 1f, 0.95f); val6.text = string.Empty; Text val7 = val.GetComponent(); if ((Object)(object)val7 == (Object)null) { val7 = val.GetComponentInChildren(true); } if ((Object)(object)val7 != (Object)null && (Object)(object)val7.font != (Object)null) { val6.font = val7.font; } else { val6.font = Resources.GetBuiltinResource("Arial.ttf"); } Outline obj = val5.AddComponent(); ((Shadow)obj).effectColor = new Color(0f, 0f, 0f, 0.9f); ((Shadow)obj).effectDistance = new Vector2(1f, -1f); val5.SetActive(false); CestusShieldOverlayImage = val3; CestusShieldOverlayRect = component2; CestusShieldReferenceRect = component; CestusShieldText = val6; CestusShieldTextRect = component3; CestusShieldOverlayLoggedMissing = false; DebugLog("[HaldorExpansion] Cestus segmented HUD with text created"); } private static Component FindReferenceHealthBarComponent(Hud hud) { FieldInfo[] fields = typeof(Hud).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Component val = null; FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { if (fieldInfo.FieldType == null || fieldInfo.FieldType.Name != "GuiBar") { continue; } object? value = fieldInfo.GetValue(hud); Component val2 = (Component)((value is Component) ? value : null); if ((Object)(object)val2 == (Object)null) { continue; } string text = fieldInfo.Name.ToLowerInvariant(); if (text.Contains("health") || text.Contains("hp")) { if (text.Contains("fast")) { return val2; } if ((Object)(object)val == (Object)null) { val = val2; } } } return val; } private static void CopyRectTransform(RectTransform source, RectTransform target) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_0056: 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) target.anchorMin = source.anchorMin; target.anchorMax = source.anchorMax; target.pivot = source.pivot; target.anchoredPosition = source.anchoredPosition; target.sizeDelta = source.sizeDelta; ((Transform)target).localScale = ((Transform)source).localScale; ((Transform)target).localRotation = ((Transform)source).localRotation; target.offsetMin = source.offsetMin; target.offsetMax = source.offsetMax; } internal static void DumpAdrenalineReflection() { if (_adrenalineReflectionDumped) { return; } _adrenalineReflectionDumped = true; try { Assembly assembly = typeof(Player).Assembly; DebugLog("[HaldorExpansion] ===== ADRENALINE / TRINKET REFLECTION DUMP START ====="); string[] needles = new string[2] { "adren", "trinket" }; Type[] array = (from t in assembly.GetTypes() where needles.Any((string n) => (t.FullName ?? string.Empty).IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0 || t.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0) orderby t.FullName select t).ToArray(); foreach (Type type in array) { DebugLog("[HaldorExpansion] [Reflect] TYPE: " + type.FullName); FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { DebugLog("[HaldorExpansion] [Reflect] FIELD: " + fieldInfo.FieldType.Name + " " + fieldInfo.Name); } PropertyInfo[] properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { DebugLog("[HaldorExpansion] [Reflect] PROP: " + propertyInfo.PropertyType.Name + " " + propertyInfo.Name); } MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo i in methods) { if (needles.Any((string n) => i.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0)) { string text = string.Join(", ", from p in i.GetParameters() select p.ParameterType.Name + " " + p.Name); DebugLog("[HaldorExpansion] [Reflect] METHOD: " + i.ReturnType.Name + " " + i.Name + "(" + text + ")"); } } } DumpRelevantMembers(typeof(Player), "Player"); DumpRelevantMembers(typeof(Hud), "Hud"); DumpRelevantMembers(typeof(Humanoid), "Humanoid"); DumpRelevantMembers(typeof(InventoryGui), "InventoryGui"); DebugLog("[HaldorExpansion] ===== ADRENALINE / TRINKET REFLECTION DUMP END ====="); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] Adrenaline reflection dump failed: {arg}"); } } } private static void DumpRelevantMembers(Type t, string label) { try { string[] source = new string[2] { "adren", "trinket" }; DebugLog("[HaldorExpansion] [Reflect] ===== " + label + " relevant members ====="); FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo f in fields) { if (source.Any((string n) => f.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0)) { DebugLog("[HaldorExpansion] [Reflect] FIELD: " + f.FieldType.Name + " " + f.Name); } } PropertyInfo[] properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo p2 in properties) { if (source.Any((string n) => p2.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0)) { DebugLog("[HaldorExpansion] [Reflect] PROP: " + p2.PropertyType.Name + " " + p2.Name); } } MethodInfo[] methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo i in methods) { if (source.Any((string n) => i.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0)) { string text = string.Join(", ", from p in i.GetParameters() select p.ParameterType.Name + " " + p.Name); DebugLog("[HaldorExpansion] [Reflect] METHOD: " + i.ReturnType.Name + " " + i.Name + "(" + text + ")"); } } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] DumpRelevantMembers failed for {label}: {arg}"); } } } internal static bool EnsureCestusReady() { //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Expected O, but got Unknown try { if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Bone_crushers"); if ((Object)(object)itemPrefab != (Object)null) { GritCestusPrefab = itemPrefab; GritCestusItemDrop = itemPrefab.GetComponent(); if ((Object)(object)GritCestusItemDrop != (Object)null) { ConfigureCestusShared(GritCestusItemDrop.m_itemData.m_shared); ApplyBoneCrushersIcon(GritCestusItemDrop.m_itemData.m_shared); GritCestusItemDrop.m_itemData.m_dropPrefab = itemPrefab; } DebugLog("[HaldorExpansion] Cestus already in ObjectDB: Bone_crushers"); return (Object)(object)GritCestusItemDrop != (Object)null; } GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("FistFenrirClaw"); if ((Object)(object)itemPrefab2 == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source cestus prefab not found: FistFenrirClaw"); } return false; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("Bone_crushers", ((Object)itemPrefab2).name); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Failed to clone cestus prefab"); } return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on cloned cestus prefab"); } return false; } ConfigureCestusShared(component.m_itemData.m_shared); ApplyBoneCrushersIcon(component.m_itemData.m_shared); component.m_itemData.m_dropPrefab = val; ItemManager.Instance.AddItem(new CustomItem(val, true)); GritCestusPrefab = val; GritCestusItemDrop = component; DebugLog("[HaldorExpansion] Cestus registered: Bone_crushers (source=" + ((Object)itemPrefab2).name + ")"); return true; } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] EnsureCestusReady error: {arg}"); } return false; } } private static void ConfigureCestusShared(SharedData shared) { if (shared != null) { shared.m_name = "$item_weapon_gritcestus"; shared.m_description = "$item_weapon_gritcestus_desc"; shared.m_maxStackSize = 1; shared.m_weight = 5f; shared.m_value = 0; shared.m_maxQuality = 1; shared.m_canBeReparied = true; shared.m_useDurability = true; shared.m_maxDurability = 1800f; shared.m_durabilityPerLevel = 0f; shared.m_damages.m_slash = 0f; shared.m_damages.m_blunt = 24f; shared.m_damages.m_pierce = 0f; shared.m_damages.m_fire = 0f; shared.m_damages.m_frost = 0f; shared.m_damages.m_lightning = 4f; shared.m_damages.m_poison = 0f; shared.m_damages.m_spirit = 0f; shared.m_damagesPerLevel.m_slash = 0f; shared.m_damagesPerLevel.m_blunt = 0f; shared.m_damagesPerLevel.m_pierce = 0f; shared.m_damagesPerLevel.m_fire = 0f; shared.m_damagesPerLevel.m_frost = 0f; shared.m_damagesPerLevel.m_lightning = 0f; shared.m_damagesPerLevel.m_poison = 0f; shared.m_damagesPerLevel.m_spirit = 0f; shared.m_blockPower = 2f; shared.m_blockPowerPerLevel = 0f; shared.m_deflectionForce = 15f; shared.m_deflectionForcePerLevel = 0f; shared.m_timedBlockBonus = 2f; if (shared.m_attack != null) { shared.m_attack.m_attackStamina = 12f; shared.m_attack.m_staggerMultiplier = 1.35f; } if (shared.m_secondaryAttack != null) { shared.m_secondaryAttack.m_attackStamina = 20f; shared.m_secondaryAttack.m_staggerMultiplier = 2.2f; } shared.m_backstabBonus = 3f; ApplyCestusEquipBonuses(shared); shared.m_canBeReparied = true; shared.m_useDurability = true; ApplySteelHeartSet(shared); } } private static void ApplyCestusEquipBonuses(SharedData shared) { if (shared != null) { ApplyCestusVisualEffect(shared); } } private static bool IsCestusWeapon(ItemData item) { if (item == null) { return false; } if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == "Bone_crushers") { return true; } if (item.m_shared != null && item.m_shared.m_name == "$item_weapon_gritcestus") { return true; } return false; } internal static void RegisterCestusRecipe() { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown try { string text = "Recipe_Bone_crushers"; if (ItemManager.Instance.GetRecipe(text) != null) { DebugLog("[HaldorExpansion] Recipe already registered: " + text); return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("FistFenrirClaw") : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source prefab not found for cestus recipe"); } return; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Source ItemDrop not found for cestus recipe"); } return; } Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); if ((Object)(object)recipe == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] Source recipe not found for cestus"); } return; } string craftingStation = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : "forge"); RecipeConfig val2 = new RecipeConfig { Name = text, Item = "Bone_crushers", Amount = 1, CraftingStation = craftingStation, RepairStation = "forge", MinStationLevel = 1, Enabled = false, Requirements = Array.Empty() }; ItemManager.Instance.AddRecipe(new CustomRecipe(val2)); DebugLog("[HaldorExpansion] Registered recipe via Jotunn for Bone_crushers"); } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] RegisterCestusRecipe error: {arg}"); } } } internal static bool FixCestusItemsInInventory(Player player) { try { if ((Object)(object)player == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Bone_crushers"); if ((Object)(object)itemPrefab == (Object)null) { return false; } ItemDrop component = itemPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } bool result = false; List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_shared != null && !(item.m_shared.m_name != "$item_weapon_gritcestus")) { result = true; int num = Mathf.Max(1, item.m_quality); float durability = item.m_durability; bool equipped = item.m_equipped; int variant = item.m_variant; long crafterID = item.m_crafterID; string crafterName = item.m_crafterName; item.m_dropPrefab = itemPrefab; item.m_shared = component.m_itemData.m_shared; item.m_quality = Mathf.Clamp(num, 1, Mathf.Max(1, item.m_shared.m_maxQuality)); item.m_variant = variant; item.m_crafterID = crafterID; item.m_crafterName = crafterName; item.m_equipped = equipped; float num2 = item.m_shared.m_maxDurability + item.m_shared.m_durabilityPerLevel * (float)(item.m_quality - 1); item.m_durability = Mathf.Clamp(durability, 0f, Mathf.Max(1f, num2)); item.m_shared.m_canBeReparied = true; item.m_shared.m_useDurability = true; } } return result; } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] FixCestusItemsInInventory error: {arg}"); } return false; } } private static bool HasEquippedCestus(Player player) { if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_equipped && IsCestusWeapon(item)) { return true; } } return false; } private static void ApplyCestusVisualEffect(SharedData shared) { if (shared != null) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = "SE_GritCestusVisual"; SetFieldIfExists(val, "m_name", "$se_weapon_gritcestus"); SetFieldIfExists(val, "m_tooltip", "$se_weapon_gritcestus_desc"); shared.m_equipStatusEffect = (StatusEffect)(object)val; } } private static void ApplyBoneCrushersIcon(SharedData shared) { if (shared != null && !TrySetEmbeddedIcon(shared, "Bone_crushers.png")) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Embedded icon missing for Bone crushers: Bone_crushers.png"); } } } internal static void DebugLog(string message) { if (DebugLogging) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } } } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; AddLocalizations(); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "aveasura.haldor.expansion"); if (DebugLogging) { DumpAdrenalineReflection(); } RegisterCestusInput(); } private static void ApplyDelayedDoomChestVisualEffect(SharedData shared) { if (shared != null) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = "SE_DelayedDoomChestVisual"; SetFieldIfExists(val, "m_name", "$se_delayeddoom_regen"); SetFieldIfExists(val, "m_tooltip", "$se_delayeddoom_regen_desc"); shared.m_equipStatusEffect = (StatusEffect)(object)val; } } internal static bool EnsureDelayedDoomChestReady() { //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Expected O, but got Unknown try { if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("ArmorDelayedDoomChest"); if ((Object)(object)itemPrefab != (Object)null) { DelayedDoomChestPrefab = itemPrefab; DelayedDoomChestItemDrop = itemPrefab.GetComponent(); if ((Object)(object)DelayedDoomChestItemDrop != (Object)null) { SharedData shared = DelayedDoomChestItemDrop.m_itemData.m_shared; ConfigureDelayedDoomChestShared(shared); ApplyDelayedDoomChestIcon(shared); DelayedDoomChestItemDrop.m_itemData.m_dropPrefab = itemPrefab; shared.m_canBeReparied = true; shared.m_useDurability = true; } DebugLog("[HaldorExpansion] Delayed Doom chest already in ObjectDB: ArmorDelayedDoomChest"); return (Object)(object)DelayedDoomChestItemDrop != (Object)null; } GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("ArmorPaddedCuirass"); if ((Object)(object)itemPrefab2 == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source chest prefab not found: ArmorPaddedCuirass"); } return false; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("ArmorDelayedDoomChest", ((Object)itemPrefab2).name); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Failed to clone delayed doom chest prefab"); } return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on delayed doom chest"); } return false; } SharedData shared2 = component.m_itemData.m_shared; ConfigureDelayedDoomChestShared(shared2); ApplyDelayedDoomChestIcon(shared2); component.m_itemData.m_dropPrefab = val; ItemManager.Instance.AddItem(new CustomItem(val, true)); DelayedDoomChestPrefab = val; DelayedDoomChestItemDrop = component; DebugLog("[HaldorExpansion] Delayed Doom chest registered: ArmorDelayedDoomChest (source=" + ((Object)itemPrefab2).name + ")"); return true; } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] EnsureDelayedDoomChestReady error: {arg}"); } return false; } } private static void ConfigureDelayedDoomChestShared(SharedData shared) { if (shared != null) { shared.m_name = "$item_chest_delayeddoom"; shared.m_description = "$item_chest_delayeddoom_desc"; shared.m_armor = 32f; shared.m_maxQuality = 1; shared.m_weight = 5f; shared.m_value = 0; shared.m_maxDurability = 2100f; shared.m_durabilityPerLevel = 0f; shared.m_useDurability = true; shared.m_canBeReparied = true; shared.m_setName = ""; shared.m_setSize = 0; shared.m_setStatusEffect = null; ApplyDelayedDoomChestVisualEffect(shared); } } internal static bool FixDelayedDoomItemsInInventory(Player player) { try { if ((Object)(object)player == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("ArmorDelayedDoomChest"); if ((Object)(object)itemPrefab == (Object)null) { return false; } ItemDrop component = itemPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } bool flag = false; bool flag2 = false; List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_shared != null && !(item.m_shared.m_name != "$item_chest_delayeddoom")) { flag = true; int num = Mathf.Max(1, item.m_quality); float durability = item.m_durability; bool equipped = item.m_equipped; int variant = item.m_variant; long crafterID = item.m_crafterID; string crafterName = item.m_crafterName; item.m_dropPrefab = itemPrefab; item.m_shared = component.m_itemData.m_shared; item.m_quality = Mathf.Clamp(num, 1, Mathf.Max(1, item.m_shared.m_maxQuality)); item.m_variant = variant; item.m_crafterID = crafterID; item.m_crafterName = crafterName; item.m_equipped = equipped; float num2 = item.m_shared.m_maxDurability + item.m_shared.m_durabilityPerLevel * (float)(item.m_quality - 1); item.m_durability = Mathf.Clamp(durability, 0f, Mathf.Max(1f, num2)); item.m_shared.m_canBeReparied = true; item.m_shared.m_useDurability = true; flag2 = true; } } if (flag) { Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); string[] obj = new string[10] { "[HaldorExpansion] Inventory rebind checked: ", $"changed={flag2}, ", "recipe=", ((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null", ", craft=", null, null, null, null, null }; object obj2; if (!((Object)(object)recipe != (Object)null)) { obj2 = "null"; } else { CraftingStation craftingStation = recipe.m_craftingStation; obj2 = ((craftingStation != null) ? ((Object)craftingStation).name : null); } obj[5] = (string)obj2; obj[6] = ", repair="; object obj3; if (!((Object)(object)recipe != (Object)null)) { obj3 = "null"; } else { CraftingStation repairStation = recipe.m_repairStation; obj3 = ((repairStation != null) ? ((Object)repairStation).name : null); } obj[7] = (string)obj3; obj[8] = ", dropPrefab="; obj[9] = ((Object)itemPrefab).name; DebugLog(string.Concat(obj)); } return flag; } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] FixDelayedDoomItemsInInventory error: {arg}"); } return false; } } internal static void RegisterDelayedDoomChestRecipe() { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown try { if (ItemManager.Instance.GetRecipe("Recipe_ArmorDelayedDoomChest") != null) { DebugLog("[HaldorExpansion] Recipe already registered: Recipe_ArmorDelayedDoomChest"); return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("ArmorPaddedCuirass") : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source prefab not found for recipe"); } return; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Source ItemDrop not found for recipe"); } return; } Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); if ((Object)(object)recipe == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] Source recipe not found"); } return; } string craftingStation = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : null); string repairStation = "forge"; RecipeConfig val2 = new RecipeConfig { Name = "Recipe_ArmorDelayedDoomChest", Item = "ArmorDelayedDoomChest", Amount = 1, CraftingStation = craftingStation, RepairStation = repairStation, MinStationLevel = 1, Enabled = false, Requirements = Array.Empty() }; ItemManager.Instance.AddRecipe(new CustomRecipe(val2)); DebugLog("[HaldorExpansion] Registered recipe via Jotunn for ArmorDelayedDoomChest"); } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] RegisterDelayedDoomChestRecipe error: {arg}"); } } } private static void ApplyDelayedDoomChestIcon(SharedData shared) { if (shared != null && !TrySetEmbeddedIcon(shared, "delayed_doom_chest.png")) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Embedded icon missing for Delayed doom chest: delayed_doom_chest.png"); } } } internal static bool HasDelayedDoomEquipped(Player player) { if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_equipped && item.m_shared != null) { string name = item.m_shared.m_name; if (!string.IsNullOrEmpty(name) && DelayedDoomItemKeys.Contains(name)) { return true; } } } return false; } internal static bool IsDelayedDoomInternalDamage(Character character) { if ((Object)(object)character != (Object)null) { return DelayedDoomInternalDamageVictims.Contains(((Object)character).GetInstanceID()); } return false; } internal static void AddDelayedDoomDebt(Player player, float delayedDamageTotal) { if (!((Object)(object)Instance == (Object)null) && !((Object)(object)player == (Object)null) && !(delayedDamageTotal <= 0f)) { int instanceID = ((Object)player).GetInstanceID(); if (!ActiveDelayedDoomDots.TryGetValue(instanceID, out var value)) { value = new DelayedDoomDotState { Player = player, RemainingDamage = 0f, TicksLeft = 0, Routine = null }; ActiveDelayedDoomDots[instanceID] = value; } value.Player = player; value.RemainingDamage += delayedDamageTotal; value.TicksLeft = 12; DebugLog("[HaldorExpansion] Delayed Doom debt added: " + $"added={delayedDamageTotal:0.##}, " + $"total={value.RemainingDamage:0.##}, " + $"ticksLeft={value.TicksLeft}, " + GetCestusDebugPlayerTag(player)); if (value.Routine == null) { DebugLog("[HaldorExpansion] Delayed Doom routine start: " + $"waitPerTick={0.5f:0.###}, " + GetCestusDebugPlayerTag(player)); value.Routine = ((MonoBehaviour)Instance).StartCoroutine(DelayedDoomRoutine(value)); } } } [IteratorStateMachine(typeof(d__173))] private static IEnumerator DelayedDoomRoutine(DelayedDoomDotState state) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__173(0) { state = state }; } internal static float GetHitTotalDamage(HitData hit) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (hit == null) { return 0f; } DamageTypes damage = hit.m_damage; return Mathf.Max(0f, damage.m_damage + damage.m_blunt + damage.m_slash + damage.m_pierce + damage.m_fire + damage.m_frost + damage.m_lightning + damage.m_poison + damage.m_spirit); } private static bool ApplyDelayedDoomHealthTick(Player player, float amount) { if ((Object)(object)player == (Object)null || amount <= 0f) { return false; } float characterHealth = GetCharacterHealth((Character)(object)player); if (characterHealth <= 0f) { return false; } float num = characterHealth - amount; DebugLog("[HaldorExpansion] Delayed Doom tick: " + $"current={characterHealth:0.##}, " + $"tick={amount:0.##}, " + $"new={num:0.##}, " + GetCestusDebugPlayerTag(player)); if (num > 0f) { SetCharacterHealth((Character)(object)player, num); return true; } ApplyLethalDelayedDoomDamage(player, characterHealth + 999f); return false; } private static void ApplyLethalDelayedDoomDamage(Player player, float amount) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown if ((Object)(object)player == (Object)null || amount <= 0f) { return; } DebugLog("[HaldorExpansion] Delayed Doom lethal tick invoke damage: " + $"amount={amount:0.##}, " + GetCestusDebugPlayerTag(player)); HitData val = new HitData(); val.m_damage.m_damage = amount; int instanceID = ((Object)player).GetInstanceID(); DelayedDoomInternalDamageVictims.Add(instanceID); try { ((Character)player).Damage(val); } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] Delayed Doom lethal damage error: {arg}"); } } finally { DelayedDoomInternalDamageVictims.Remove(instanceID); } } internal static float GetPlayerBaseFoodRegen(Player player) { if ((Object)(object)player == (Object)null) { return 0f; } List foods = player.GetFoods(); if (foods == null) { return 0f; } float num = 0f; foreach (Food item in foods) { if (item?.m_item?.m_shared != null) { num += Mathf.Max(0f, item.m_item.m_shared.m_foodRegen); } } return num; } private static bool TrySetEmbeddedIcon(SharedData shared, string iconFileName) { if (shared == null) { return false; } Sprite val = LoadEmbeddedIcon("HaldorExpansion.Assets.Icons." + iconFileName); if ((Object)(object)val == (Object)null) { return false; } shared.m_icons = (Sprite[])(object)new Sprite[1] { val }; DebugLog("[HaldorExpansion] Embedded icon applied: " + iconFileName); return true; } private static Sprite LoadEmbeddedIcon(string resourceName) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); if (stream == null) { DebugLog("[HaldorExpansion] Embedded icon not found: " + resourceName); return null; } byte[] bytes = ReadAllBytes(stream); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!TryLoadTextureImage(val, bytes)) { DebugLog("[HaldorExpansion] Failed to load embedded icon image: " + resourceName); return null; } ((Object)val).name = resourceName; return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); } catch (Exception ex) { DebugLog("[HaldorExpansion] Failed to load embedded icon " + resourceName + ": " + ex); return null; } } private static bool TryLoadTextureImage(Texture2D texture, byte[] bytes) { try { Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule"); if (type == null) { DebugLog("[HaldorExpansion] UnityEngine.ImageConversion type not found"); return false; } MethodInfo method = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }, null); if (method != null) { object obj = method.Invoke(null, new object[3] { texture, bytes, false }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } method = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Texture2D), typeof(byte[]) }, null); if (method != null) { object obj2 = method.Invoke(null, new object[2] { texture, bytes }); bool flag2 = default(bool); int num2; if (obj2 is bool) { flag2 = (bool)obj2; num2 = 1; } else { num2 = 0; } return (byte)((uint)num2 & (flag2 ? 1u : 0u)) != 0; } DebugLog("[HaldorExpansion] UnityEngine.ImageConversion.LoadImage method not found"); return false; } catch (Exception ex) { DebugLog("[HaldorExpansion] TryLoadTextureImage error: " + ex); return false; } } private static byte[] ReadAllBytes(Stream stream) { using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); return memoryStream.ToArray(); } private void AddLocalizations() { CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "Russian"; localization.AddTranslation(ref text, new Dictionary { ["$item_brisingamen"] = "Осквернённый Бризингамен", ["$item_brisingamen_desc"] = "Легендарное золотое ожерелье, чья древняя сила облегчает даже самую тяжёлую ношу.", ["$item_chest_delayeddoom"] = "Кираса безмолвной расплаты", ["$item_chest_delayeddoom_desc"] = "Если удар после брони должен снести больше половины максимального здоровья, только часть урона проходит сразу, а остальное возвращается отложенной болью через несколько секунд.\nОна не спасает от расплаты — лишь даёт время нанести ответный удар.", ["$se_delayeddoom_regen"] = "Безмолвная живучесть", ["$se_delayeddoom_regen_desc"] = "+5 к восстановлению здоровья.", ["$item_crossbow_silenthunt"] = "Арбалет теневой охоты", ["$item_crossbow_silenthunt_desc"] = "Тяжёлый арбалет для тех, кто предпочитает решать бой первым и единственным выстрелом. Особенно беспощаден к целям, которые ещё не поняли, что на них уже открыли охоту.", ["$item_weapon_gritcestus"] = "Костоломы", ["$item_weapon_gritcestus_desc"] = "Рождённые в грязи и закалённые кровью, эти кастеты признают лишь один закон: кто выстоял под ударом, тот и забирает победу.\nНи одна стойка врага не выдержит их бешеного ритма ударов.\nПолучая урон, владелец накапливает боевой запал.\nАктивное умение: при полном запасе боевого запала нажмите колесико мыши, чтобы выпустить круговой выброс силы и получить краткий защитный покров.", ["$se_weapon_gritcestus"] = "Закон арены", ["$se_weapon_gritcestus_desc"] = "+30 к максимуму здоровья.\n+90% к скорости атаки.\n-15 брони.", ["$item_chest_pitking"] = "Накидка хозяина ямы", ["$item_chest_pitking_desc"] = "Её не создавали для защиты. Она создана для тех, кто выходит на арену не прятаться за сталью, а ломать кости голыми руками.", ["$se_pitking_vigor"] = "Воля хозяина ямы", ["$se_pitking_vigor_desc"] = "+70 к максимуму здоровья.", ["$itemset_steelheart"] = "Стальное сердце", ["$itemset_steelheart_desc"] = "Сет с кастетом: активка кастета получает на 50% больше силы щита и урона ударной волны.", ["$item_cape_woundedbeast"] = "Плащ раненого зверя", ["$item_cape_woundedbeast_desc"] = "Пока кровь ещё не остыла, зверь отказывается падать.\nЧем ближе смерть, тем яростнее он цепляется за жизнь — но каждая рана вгрызается глубже.", ["$se_cape_woundedbeast"] = "Раненый зверь", ["$se_cape_woundedbeast_desc"] = "Получаемый урон +15%.\nРаз в секунду восстанавливает здоровье: чем ниже текущее здоровье, тем сильнее восстановление.\nОгонь, яд и дух подавляют регенерацию на 2 секунды.\n\nПри использовании вместе с Кирасой безмолвной расплаты эффект лечения сокращается в 2 раза.", ["$item_cape_fracturedbreath"] = "Плащ выжженной стойкости", ["$item_cape_fracturedbreath_desc"] = "Часть полученного после брони урона принимает на себя выносливость. Но когда силы иссякают, расплата становится тяжелее.", ["$se_cape_fracturedbreath"] = "Выжженная стойкость", ["$se_cape_fracturedbreath_desc"] = "15% полученного после брони урона уходит в выносливость, но плата за эту отсрочку — усиленное истощение. Если выносливости не хватает, недостающий урон проходит по здоровью на 30% сильнее." }); text = "English"; localization.AddTranslation(ref text, new Dictionary { ["$item_brisingamen"] = "Corrupted Brisingamen", ["$item_brisingamen_desc"] = "A legendary golden necklace whose ancient power makes even the heaviest burden easier to bear.", ["$item_chest_delayeddoom"] = "Cuirass of Silent Reckoning", ["$item_chest_delayeddoom_desc"] = "If a post-armor hit would take more than half of your maximum health, only part of the damage is taken immediately, while the rest returns as delayed pain over the next few seconds.\nIt does not save you from the reckoning — it only gives you time to strike back.", ["$se_delayeddoom_regen"] = "Silent Vitality", ["$se_delayeddoom_regen_desc"] = "+5 health regeneration.", ["$item_crossbow_silenthunt"] = "Crossbow of the Shadow Hunt", ["$item_crossbow_silenthunt_desc"] = "A heavy crossbow for those who prefer to decide the battle with the first and only shot. Especially ruthless against targets that do not yet realize the hunt has already begun.", ["$item_weapon_gritcestus"] = "Bone Crushers", ["$item_weapon_gritcestus_desc"] = "Born in the mud and tempered in blood, these cestus obey only one law: the one who endures the blow claims the victory.\nNo enemy stance can withstand their savage rhythm of strikes.\nTaking damage builds battle fervor.\nActive ability: when battle fervor is full, press the middle mouse button to release a circular burst of force and gain a brief protective barrier.", ["$se_weapon_gritcestus"] = "Law of the Arena", ["$se_weapon_gritcestus_desc"] = "+30 to maximum health.\n+90% attack speed.\n-15 armor.", ["$item_chest_pitking"] = "Pit King's Cuirass", ["$item_chest_pitking_desc"] = "It was not forged for protection. It was made for those who step into the pit not to hide behind steel, but to break bones with bare hands.", ["$se_pitking_vigor"] = "Pit King's Vigor", ["$se_pitking_vigor_desc"] = "+70 to maximum health.", ["$itemset_steelheart"] = "Steel Heart", ["$itemset_steelheart_desc"] = "Set with the cestus: the cestus active ability gains 50% more barrier strength and shockwave damage.", ["$item_cape_woundedbeast"] = "Cloak of the Wounded Beast", ["$item_cape_woundedbeast_desc"] = "While the blood still burns, the beast refuses to fall.\nThe closer death comes, the more fiercely it clings to life — but every wound bites deeper.", ["$se_cape_woundedbeast"] = "Wounded Beast", ["$se_cape_woundedbeast_desc"] = "Incoming damage +15%.\nRestores health once per second: the lower your current health is, the stronger the regeneration becomes.\nFire, poison, and spirit suppress regeneration for 2 seconds.\n\nWhen used together with the Cuirass of Silent Reckoning, its healing effect is halved.", ["$item_cape_fracturedbreath"] = "Cloak of Burned Resolve", ["$item_cape_fracturedbreath_desc"] = "Part of the damage taken after armor is absorbed by stamina. But when strength runs dry, the reckoning grows harsher.", ["$se_cape_fracturedbreath"] = "Burned Resolve", ["$se_cape_fracturedbreath_desc"] = "15% of damage taken after armor is redirected into stamina, but this delayed pain drains stamina 50% harder. If you do not have enough stamina, the missing damage hits health 30% harder." }); } private static void ApplyPitKingChestVisualEffect(SharedData shared) { if (shared != null) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = "SE_PitKingChestVisual"; SetFieldIfExists(val, "m_name", "$se_pitking_vigor"); SetFieldIfExists(val, "m_tooltip", "$se_pitking_vigor_desc"); shared.m_equipStatusEffect = (StatusEffect)(object)val; } } internal static void ApplySteelHeartSet(SharedData shared) { if (shared != null) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = "SE_SteelHeartSet"; SetFieldIfExists(val, "m_name", "$itemset_steelheart"); SetFieldIfExists(val, "m_tooltip", "$itemset_steelheart_desc"); shared.m_setName = "$itemset_steelheart"; shared.m_setSize = 2; shared.m_setStatusEffect = (StatusEffect)(object)val; } } internal static bool EnsurePitKingChestReady() { //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Expected O, but got Unknown try { if ((Object)(object)PitKingChestItemDrop != (Object)null && (Object)(object)PitKingChestPrefab != (Object)null) { return true; } if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("ArmorPitKingChest"); if ((Object)(object)itemPrefab != (Object)null) { PitKingChestPrefab = itemPrefab; PitKingChestItemDrop = itemPrefab.GetComponent(); if ((Object)(object)PitKingChestItemDrop != (Object)null) { ConfigurePitKingChestShared(PitKingChestItemDrop.m_itemData.m_shared); ApplyPitKingChestIcon(PitKingChestItemDrop.m_itemData.m_shared); } DebugLog("[HaldorExpansion] Pit King chest already in ObjectDB: ArmorPitKingChest"); return (Object)(object)PitKingChestItemDrop != (Object)null; } GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("ArmorMageChest"); if ((Object)(object)itemPrefab2 == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source chest prefab not found: ArmorMageChest"); } return false; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("ArmorPitKingChest", ((Object)itemPrefab2).name); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Failed to clone Pit King chest prefab"); } return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on cloned Pit King chest prefab"); } return false; } ConfigurePitKingChestShared(component.m_itemData.m_shared); ApplyPitKingChestIcon(component.m_itemData.m_shared); component.m_itemData.m_dropPrefab = val; ItemManager.Instance.AddItem(new CustomItem(val, true)); PitKingChestPrefab = val; PitKingChestItemDrop = component; DebugLog("[HaldorExpansion] Pit King chest registered: ArmorPitKingChest (source=" + ((Object)itemPrefab2).name + ")"); return true; } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] EnsurePitKingChestReady error: {arg}"); } return false; } } private static void ConfigurePitKingChestShared(SharedData shared) { if (shared != null) { shared.m_name = "$item_chest_pitking"; shared.m_description = "$item_chest_pitking_desc"; shared.m_armor = 12f; shared.m_maxQuality = 1; shared.m_weight = 5f; shared.m_value = 0; shared.m_maxDurability = 2100f; shared.m_durabilityPerLevel = 0f; shared.m_useDurability = true; shared.m_canBeReparied = true; shared.m_eitrRegenModifier = 0f; shared.m_movementModifier = -0.05f; shared.m_setName = ""; shared.m_setSize = 0; shared.m_setStatusEffect = null; ApplyPitKingChestVisualEffect(shared); ApplySteelHeartSet(shared); } } internal static bool HasPitKingChestEquipped(Player player) { if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_equipped && item.m_shared != null) { string name = item.m_shared.m_name; if (!string.IsNullOrEmpty(name) && PitKingChestItemKeys.Contains(name)) { return true; } } } return false; } internal static void RegisterPitKingChestRecipe() { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Expected O, but got Unknown try { if (ItemManager.Instance.GetRecipe("Recipe_ArmorPitKingChest") != null) { DebugLog("[HaldorExpansion] Recipe already registered: Recipe_ArmorPitKingChest"); return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("ArmorMageChest") : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source prefab not found for Pit King recipe"); } return; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Source ItemDrop not found for Pit King recipe"); } return; } Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); if ((Object)(object)recipe == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] Source recipe not found for Pit King chest"); } return; } string craftingStation = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : null); string repairStation = "forge"; RecipeConfig val2 = new RecipeConfig { Name = "Recipe_ArmorPitKingChest", Item = "ArmorPitKingChest", Amount = 1, CraftingStation = craftingStation, RepairStation = repairStation, MinStationLevel = 1, Enabled = false, Requirements = Array.Empty() }; ItemManager.Instance.AddRecipe(new CustomRecipe(val2)); DebugLog("[HaldorExpansion] Registered recipe via Jotunn for ArmorPitKingChest"); } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] RegisterPitKingChestRecipe error: {arg}"); } } } private static void ApplyPitKingChestIcon(SharedData shared) { if (shared != null && !TrySetEmbeddedIcon(shared, "pit_king_chest.png")) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Embedded icon missing for Pit King Chest: pit_king_chest.png"); } } } private static void SetFieldIfExists(object obj, string fieldName, object value) { FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, value); } } private static void CopyFieldIfExists(object src, object dst, string fieldName) { FieldInfo field = src.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); FieldInfo field2 = dst.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field2 != null) { object value = field.GetValue(src); field2.SetValue(dst, value); } } internal static float GetCharacterMaxHealth(Character character) { return InvokeFloatMethod(character, "GetMaxHealth"); } internal static float GetCharacterHealth(Character character) { return InvokeFloatMethod(character, "GetHealth"); } internal static void SetCharacterHealth(Character character, float health) { if ((Object)(object)character == (Object)null) { return; } health = Mathf.Max(0f, health); Type type = ((object)character).GetType(); while (type != null) { MethodInfo method = type.GetMethod("SetHealth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(float) }, null); if (method != null) { method.Invoke(character, new object[1] { health }); return; } type = type.BaseType; } FieldInfo field = ((object)character).GetType().GetField("m_health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(character, health); } } private static float InvokeFloatMethod(object target, string methodName) { if (target == null) { return 0f; } Type type = target.GetType(); while (type != null) { MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null && method.ReturnType == typeof(float)) { object obj = method.Invoke(target, null); if (obj is float) { return (float)obj; } } type = type.BaseType; } return 0f; } private static object GetFieldValueObject(object instance, string fieldName) { if (instance == null || string.IsNullOrEmpty(fieldName)) { return null; } Type type = instance.GetType(); while (type != null) { FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(instance); } type = type.BaseType; } return null; } private static T GetFieldValue(object instance, string fieldName) { object fieldValueObject = GetFieldValueObject(instance, fieldName); if (fieldValueObject == null) { return default(T); } if (fieldValueObject is T) { return (T)fieldValueObject; } return default(T); } internal static void ResetItemCaches() { RingPrefab = null; RingItemDrop = null; DelayedDoomChestPrefab = null; DelayedDoomChestItemDrop = null; PitKingChestPrefab = null; PitKingChestItemDrop = null; GritCestusPrefab = null; GritCestusItemDrop = null; ShadowCrossbowPrefab = null; ShadowCrossbowItemDrop = null; ResetWoundedBeastCapeState(); _ringReadyLogged = false; } internal static bool EnsureRingReady() { //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Expected O, but got Unknown //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)RingItemDrop != (Object)null && (Object)(object)RingPrefab != (Object)null) { return true; } if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("BrisingamenRing"); if ((Object)(object)itemPrefab != (Object)null) { RingPrefab = itemPrefab; RingItemDrop = itemPrefab.GetComponent(); if ((Object)(object)RingItemDrop != (Object)null) { ApplyBrisingamenIcon(RingItemDrop.m_itemData.m_shared); } if (!_ringReadyLogged && (Object)(object)RingItemDrop != (Object)null) { _ringReadyLogged = true; DebugLog("[HaldorExpansion] Ring already in ObjectDB: BrisingamenRing"); } return (Object)(object)RingItemDrop != (Object)null; } GameObject val = FindCarryWeightSource(ObjectDB.instance); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Carry-weight source item not found in ObjectDB (yet)"); } return false; } GameObject val2 = PrefabManager.Instance.CreateClonedPrefab("BrisingamenRing", ((Object)val).name); if ((Object)(object)val2 == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] CreateClonedPrefab returned null"); } return false; } ItemDrop component = val2.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on cloned prefab"); } return false; } SharedData shared = component.m_itemData.m_shared; shared.m_name = "$item_brisingamen"; shared.m_description = "$item_brisingamen_desc"; shared.m_setName = ""; shared.m_setSize = 0; shared.m_setStatusEffect = null; StatusEffect equipStatusEffect = shared.m_equipStatusEffect; SE_Stats val3 = (SE_Stats)(object)((equipStatusEffect is SE_Stats) ? equipStatusEffect : null); SE_Stats val4 = ScriptableObject.CreateInstance(); ((Object)val4).name = "SE_BrisingamenCarryWeight"; if ((Object)(object)val3 != (Object)null) { CopyFieldIfExists(val3, val4, "m_icon"); CopyFieldIfExists(val3, val4, "m_startMessage"); CopyFieldIfExists(val3, val4, "m_stopMessage"); CopyFieldIfExists(val3, val4, "m_startMessageType"); CopyFieldIfExists(val3, val4, "m_stopMessageType"); } val4.m_addMaxCarryWeight = 150f; SetFieldIfExists(val4, "m_name", "$item_brisingamen"); SetFieldIfExists(val4, "m_tooltip", "$item_brisingamen_desc"); shared.m_equipStatusEffect = (StatusEffect)(object)val4; GameObject val5 = null; if (PrefabManager.Instance != null) { val5 = PrefabManager.Instance.GetPrefab("TrinketBronzeStamina"); } if ((Object)(object)val5 != (Object)null) { ItemDrop component2 = val5.GetComponent(); if ((Object)(object)component2 != (Object)null) { shared.m_itemType = component2.m_itemData.m_shared.m_itemType; } } ApplyBrisingamenIcon(shared); ItemManager.Instance.AddItem(new CustomItem(val2, true)); RingPrefab = val2; RingItemDrop = component; if (!_ringReadyLogged) { _ringReadyLogged = true; DebugLog("[HaldorExpansion] Ring registered: BrisingamenRing (source=" + ((Object)val).name + ")"); } return true; } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] EnsureRingReady error: {arg}"); } return false; } } private static GameObject FindCarryWeightSource(ObjectDB db) { if ((Object)(object)db == (Object)null || db.m_items == null) { return null; } foreach (GameObject item in db.m_items) { if (!((Object)(object)item == (Object)null)) { ItemDrop component = item.GetComponent(); if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null && component.m_itemData.m_shared.m_name == "$item_megingjord") { return item; } } } foreach (GameObject item2 in db.m_items) { if ((Object)(object)item2 == (Object)null) { continue; } ItemDrop component2 = item2.GetComponent(); if (!((Object)(object)component2 == (Object)null)) { SharedData val = ((component2.m_itemData != null) ? component2.m_itemData.m_shared : null); SE_Stats val2 = (SE_Stats)((val != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val2 != (Object)null && val2.m_addMaxCarryWeight > 0f) { return item2; } } } return null; } private static void ApplyBrisingamenIcon(SharedData shared) { if (shared != null && !TrySetEmbeddedIcon(shared, "brisingamen.png")) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Embedded icon missing for brisingamen: brisingamen.png"); } } } internal static bool EnsureShadowCrossbowReady() { //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Expected O, but got Unknown try { if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("CrossbowSilentHunt"); if ((Object)(object)itemPrefab != (Object)null) { ShadowCrossbowPrefab = itemPrefab; ShadowCrossbowItemDrop = itemPrefab.GetComponent(); if ((Object)(object)ShadowCrossbowItemDrop != (Object)null) { ConfigureShadowCrossbowShared(ShadowCrossbowItemDrop.m_itemData.m_shared); ApplyShadowCrossbowIcon(ShadowCrossbowItemDrop.m_itemData.m_shared); } DebugLog("[HaldorExpansion] Shadow crossbow already in ObjectDB: CrossbowSilentHunt"); return (Object)(object)ShadowCrossbowItemDrop != (Object)null; } GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("CrossbowArbalest"); if ((Object)(object)itemPrefab2 == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source crossbow prefab not found: CrossbowArbalest"); } return false; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("CrossbowSilentHunt", ((Object)itemPrefab2).name); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Failed to clone shadow crossbow prefab"); } return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on shadow crossbow"); } return false; } ConfigureShadowCrossbowShared(component.m_itemData.m_shared); ApplyShadowCrossbowIcon(component.m_itemData.m_shared); component.m_itemData.m_dropPrefab = val; ItemManager.Instance.AddItem(new CustomItem(val, true)); ShadowCrossbowPrefab = val; ShadowCrossbowItemDrop = component; DebugLog("[HaldorExpansion] Shadow crossbow registered: CrossbowSilentHunt (source=" + ((Object)itemPrefab2).name + ")"); return true; } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] EnsureShadowCrossbowReady error: {arg}"); } return false; } } private static void ConfigureShadowCrossbowShared(SharedData shared) { //IL_0036: 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_007e: Unknown result type (might be due to invalid IL or missing references) if (shared != null) { shared.m_name = "$item_crossbow_silenthunt"; shared.m_description = "$item_crossbow_silenthunt_desc"; shared.m_maxQuality = 1; shared.m_weight = 4f; shared.m_value = 0; shared.m_skillType = (SkillType)14; shared.m_movementModifier = -0.35f; shared.m_backstabBonus = 6f; shared.m_attackForce = 80f; shared.m_damages = default(DamageTypes); shared.m_damages.m_pierce = 210f; shared.m_damagesPerLevel = default(DamageTypes); shared.m_maxDurability = 800f; shared.m_durabilityPerLevel = 0f; shared.m_useDurability = true; shared.m_canBeReparied = true; shared.m_setName = ""; shared.m_setSize = 0; shared.m_setStatusEffect = null; } } internal static void RegisterShadowCrossbowRecipe() { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown try { string text = "Recipe_CrossbowSilentHunt"; if (ItemManager.Instance.GetRecipe(text) != null) { DebugLog("[HaldorExpansion] Recipe already registered: " + text); return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("CrossbowArbalest") : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source prefab not found for shadow crossbow recipe"); } return; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Source ItemDrop not found for shadow crossbow recipe"); } return; } Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); if ((Object)(object)recipe == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] Source recipe not found for shadow crossbow"); } return; } string craftingStation = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : null); string repairStation = "forge"; RecipeConfig val2 = new RecipeConfig { Name = text, Item = "CrossbowSilentHunt", Amount = 1, CraftingStation = craftingStation, RepairStation = repairStation, MinStationLevel = 1, Enabled = false, Requirements = Array.Empty() }; ItemManager.Instance.AddRecipe(new CustomRecipe(val2)); DebugLog("[HaldorExpansion] Registered recipe via Jotunn for CrossbowSilentHunt"); } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] RegisterShadowCrossbowRecipe error: {arg}"); } } } private static void ApplyShadowCrossbowIcon(SharedData shared) { if (shared != null && !TrySetEmbeddedIcon(shared, "Shadow_crossbow.png")) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Embedded icon missing for Shadow Crossbow: Shadow_crossbow.png"); } } } internal static void ApplyTrophyValues(ObjectDB db) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Invalid comparison between Unknown and I4 if ((Object)(object)db == (Object)null || db.m_items == null) { return; } foreach (GameObject item in db.m_items) { if ((Object)(object)item == (Object)null) { continue; } ItemDrop component = item.GetComponent(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { continue; } SharedData shared = component.m_itemData.m_shared; if (ExtraSellableItems.TryGetValue(((Object)item).name, out var value)) { shared.m_value = value; DebugLog($"[HaldorExpansion] Extra sellable item value set: prefab={((Object)item).name}, key={shared.m_name}, value={value}"); } else if ((int)shared.m_itemType == 13) { int num = 10; if (TrophyValueOverrides.TryGetValue(((Object)item).name, out var value2)) { num = value2; } shared.m_value = num; DebugLog($"[HaldorExpansion] Trophy value set: prefab={((Object)item).name}, key={shared.m_name}, value={num}"); } } } internal static bool IsSellableToHaldor(ItemData item) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 if (item == null || item.m_shared == null) { return false; } string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : null); if (string.IsNullOrEmpty(text)) { return false; } if (ExtraSellableItems.ContainsKey(text)) { return true; } if ((int)item.m_shared.m_itemType == 13) { return item.m_shared.m_value > 0; } return false; } internal static void ResetWoundedBeastCapeState() { WoundedBeastCapePrefab = null; WoundedBeastCapeItemDrop = null; WoundedBeastCapeNextRegenTickTimes.Clear(); WoundedBeastCapeRegenSuppressedUntil.Clear(); } internal static bool HasWoundedBeastCapeEquipped(Player player) { if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_equipped && item.m_shared != null && item.m_shared.m_name == "$item_cape_woundedbeast") { return true; } } return false; } internal static bool EnsureWoundedBeastCapeReady() { //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown try { if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("CapeWoundedBeast"); if ((Object)(object)itemPrefab != (Object)null) { WoundedBeastCapePrefab = itemPrefab; WoundedBeastCapeItemDrop = itemPrefab.GetComponent(); if ((Object)(object)WoundedBeastCapeItemDrop != (Object)null) { ConfigureWoundedBeastCapeShared(WoundedBeastCapeItemDrop.m_itemData.m_shared); WoundedBeastCapeItemDrop.m_itemData.m_dropPrefab = itemPrefab; } DebugLog("[HaldorExpansion] Wounded Beast cape already in ObjectDB: CapeWoundedBeast"); return (Object)(object)WoundedBeastCapeItemDrop != (Object)null; } GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("CapeWolf"); if ((Object)(object)itemPrefab2 == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source cape prefab not found: CapeWolf"); } return false; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("CapeWoundedBeast", ((Object)itemPrefab2).name); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Failed to clone Wounded Beast cape prefab"); } return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on Wounded Beast cape"); } return false; } ConfigureWoundedBeastCapeShared(component.m_itemData.m_shared); component.m_itemData.m_dropPrefab = val; ItemManager.Instance.AddItem(new CustomItem(val, true)); EnsureWoundedBeastCapeRegisteredInObjectDB(val); WoundedBeastCapePrefab = val; WoundedBeastCapeItemDrop = component; DebugLog("[HaldorExpansion] Wounded Beast cape registered: CapeWoundedBeast (source=" + ((Object)itemPrefab2).name + ")"); return true; } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] EnsureWoundedBeastCapeReady error: {arg}"); } return false; } } internal static void RegisterWoundedBeastCapeRecipe() { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013b: 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) //IL_014f: Expected O, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Expected O, but got Unknown try { string text = "Recipe_CapeWoundedBeast"; if (ItemManager.Instance.GetRecipe(text) != null) { DebugLog("[HaldorExpansion] Recipe already registered: " + text); return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("CapeWolf") : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source cape prefab not found for Wounded Beast recipe"); } return; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Source cape ItemDrop not found for Wounded Beast recipe"); } return; } Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); if ((Object)(object)recipe == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] Source cape recipe not found for Wounded Beast cape"); } return; } string text2 = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : null); string repairStation = (((Object)(object)recipe.m_repairStation != (Object)null) ? ((Object)recipe.m_repairStation).name : (text2 ?? "forge")); RecipeConfig val2 = new RecipeConfig { Name = text, Item = "CapeWoundedBeast", Amount = 1, CraftingStation = text2, RepairStation = repairStation, MinStationLevel = 1, Enabled = false, Requirements = Array.Empty() }; ItemManager.Instance.AddRecipe(new CustomRecipe(val2)); DebugLog("[HaldorExpansion] Registered recipe via Jotunn for CapeWoundedBeast"); } catch (Exception arg) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] RegisterWoundedBeastCapeRecipe error: {arg}"); } } } internal static bool FixWoundedBeastCapeItemsInInventory(Player player) { try { if ((Object)(object)player == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("CapeWoundedBeast"); if ((Object)(object)itemPrefab == (Object)null) { return false; } ItemDrop component = itemPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } bool result = false; List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_shared != null && !(item.m_shared.m_name != "$item_cape_woundedbeast")) { result = true; int num = Mathf.Max(1, item.m_quality); float durability = item.m_durability; bool equipped = item.m_equipped; int variant = item.m_variant; long crafterID = item.m_crafterID; string crafterName = item.m_crafterName; item.m_dropPrefab = itemPrefab; item.m_shared = component.m_itemData.m_shared; item.m_quality = Mathf.Clamp(num, 1, Mathf.Max(1, item.m_shared.m_maxQuality)); item.m_variant = variant; item.m_crafterID = crafterID; item.m_crafterName = crafterName; item.m_equipped = equipped; float num2 = item.m_shared.m_maxDurability + item.m_shared.m_durabilityPerLevel * (float)(item.m_quality - 1); item.m_durability = Mathf.Clamp(durability, 0f, Mathf.Max(1f, num2)); item.m_shared.m_canBeReparied = true; item.m_shared.m_useDurability = true; } } return result; } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] FixWoundedBeastCapeItemsInInventory error: {arg}"); } return false; } } internal static void UpdateWoundedBeastCapeRegen(Player player) { if ((Object)(object)player == (Object)null) { return; } long playerID = player.GetPlayerID(); if (!HasWoundedBeastCapeEquipped(player) || ((Character)player).IsDead()) { WoundedBeastCapeNextRegenTickTimes.Remove(playerID); return; } float time = Time.time; if ((!WoundedBeastCapeRegenSuppressedUntil.TryGetValue(playerID, out var value) || !(time < value)) && (!WoundedBeastCapeNextRegenTickTimes.TryGetValue(playerID, out var value2) || time >= value2)) { WoundedBeastCapeNextRegenTickTimes[playerID] = time + 1f; TryApplyWoundedBeastCapeRegenTick(player); } } internal static void ApplyWoundedBeastCapeIncomingDamagePenalty(Player player, HitData hit) { if (!((Object)(object)player == (Object)null) && hit != null && HasWoundedBeastCapeEquipped(player)) { ScaleWoundedBeastCapeDamageTypes(ref hit.m_damage, 1.15f); if (HasWoundedBeastCapeSuppressionDamage(hit)) { long playerID = player.GetPlayerID(); WoundedBeastCapeRegenSuppressedUntil[playerID] = Time.time + 2f; } } } private static void TryApplyWoundedBeastCapeRegenTick(Player player) { float characterHealth = GetCharacterHealth((Character)(object)player); float characterMaxHealth = GetCharacterMaxHealth((Character)(object)player); if (characterHealth <= 0f || characterMaxHealth <= 0f || characterHealth >= characterMaxHealth) { return; } float num = GetWoundedBeastCapeRegenPerSecond(characterHealth, characterMaxHealth); if (!(num <= 0f)) { if (HasDelayedDoomEquipped(player)) { num *= 0.5f; } float num2 = Mathf.Min(characterMaxHealth, characterHealth + num); if (!(num2 <= characterHealth + 0.001f)) { SetCharacterHealth((Character)(object)player, num2); DebugLog("[HaldorExpansion] Wounded Beast regen tick: " + $"current={characterHealth:0.##}, " + $"max={characterMaxHealth:0.##}, " + $"regen={num:0.##}, " + $"new={num2:0.##}, " + $"delayedDoom={HasDelayedDoomEquipped(player)}"); } } } private static float GetWoundedBeastCapeRegenPerSecond(float currentHealth, float maxHealth) { float num = currentHealth / maxHealth; if (num > 0.8f) { return 0f; } if (num > 0.7f) { return 1f; } if (num > 0.6f) { return 2f; } if (num > 0.55f) { return 3f; } if (num > 0.4f) { return 5f; } if (num > 0.2f) { return 7f; } return 10f; } private static bool HasWoundedBeastCapeSuppressionDamage(HitData hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) DamageTypes damage = hit.m_damage; if (!(damage.m_fire > 0f) && !(damage.m_poison > 0f)) { return damage.m_spirit > 0f; } return true; } private static void ScaleWoundedBeastCapeDamageTypes(ref DamageTypes damage, float multiplier) { damage.m_damage *= multiplier; damage.m_blunt *= multiplier; damage.m_slash *= multiplier; damage.m_pierce *= multiplier; damage.m_fire *= multiplier; damage.m_frost *= multiplier; damage.m_lightning *= multiplier; damage.m_poison *= multiplier; damage.m_spirit *= multiplier; } private static void EnsureWoundedBeastCapeRegisteredInObjectDB(GameObject itemPrefab) { try { if (!((Object)(object)ObjectDB.instance == (Object)null) && !((Object)(object)itemPrefab == (Object)null)) { if (ObjectDB.instance.m_items != null && !ObjectDB.instance.m_items.Contains(itemPrefab)) { ObjectDB.instance.m_items.Add(itemPrefab); } typeof(ObjectDB).GetMethod("UpdateItemHashes", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(ObjectDB.instance, null); } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] EnsureWoundedBeastCapeRegisteredInObjectDB error: {arg}"); } } } private static void ConfigureWoundedBeastCapeShared(SharedData shared) { if (shared == null) { return; } shared.m_name = "$item_cape_woundedbeast"; shared.m_description = "$item_cape_woundedbeast_desc"; shared.m_armor = 1f; shared.m_maxQuality = 1; shared.m_weight = 1f; shared.m_value = 0; shared.m_maxDurability = 1800f; shared.m_durabilityPerLevel = 0f; shared.m_canBeReparied = true; shared.m_useDurability = true; shared.m_setName = ""; shared.m_setSize = 0; shared.m_setStatusEffect = null; if (shared.m_damageModifiers != null) { shared.m_damageModifiers.RemoveAll((DamageModPair mod) => (int)mod.m_type == 64); } ApplyWoundedBeastCapeVisualEffect(shared); } private static void ApplyWoundedBeastCapeVisualEffect(SharedData shared) { if (shared != null) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = "SE_WoundedBeastCapeVisual"; SetFieldIfExists(val, "m_name", "$se_cape_woundedbeast"); SetFieldIfExists(val, "m_tooltip", "$se_cape_woundedbeast_desc"); shared.m_equipStatusEffect = (StatusEffect)(object)val; } } } [HarmonyPatch(typeof(Player), "Update")] internal static class WoundedBeastCapePlayerUpdatePatch { private static readonly HashSet ProcessedPlayers = new HashSet(); private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)ObjectDB.instance == (Object)null)) { int instanceID = ((Object)__instance).GetInstanceID(); if (!ProcessedPlayers.Contains(instanceID)) { HaldorExpansionPlugin.FixWoundedBeastCapeItemsInInventory(__instance); ProcessedPlayers.Add(instanceID); } HaldorExpansionPlugin.UpdateWoundedBeastCapeRegen(__instance); } } } internal static class StaminaCapeFeature { internal const string PrefabName = "CapeFracturedBreath"; internal const string SourcePrefab = "CapeLox"; internal const int Price = 2500; internal const float DamageToStaminaRatio = 0.15f; internal const float OverflowDamageMultiplier = 1.3f; internal const float StaminaCostMultiplier = 1.5f; internal const string ItemKey = "$item_cape_fracturedbreath"; internal const string ItemDescKey = "$item_cape_fracturedbreath_desc"; internal const string EffectNameKey = "$se_cape_fracturedbreath"; internal const string EffectDescKey = "$se_cape_fracturedbreath_desc"; internal static GameObject CapePrefab; internal static ItemDrop CapeItemDrop; internal static void ResetCaches() { CapePrefab = null; CapeItemDrop = null; } internal static bool HasEquipped(Player player) { if ((Object)(object)player == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_equipped && item.m_shared != null && item.m_shared.m_name == "$item_cape_fracturedbreath") { return true; } } return false; } internal static float GetPlayerStamina(Player player) { return InvokeFloatMethod(player, "GetStamina"); } internal static void SpendPlayerStamina(Player player, float amount) { if ((Object)(object)player == (Object)null || amount <= 0f) { return; } amount = Mathf.Max(0f, amount); Type type = ((object)player).GetType(); while (type != null) { MethodInfo method = type.GetMethod("UseStamina", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(float) }, null); if (method != null) { method.Invoke(player, new object[1] { amount }); return; } type = type.BaseType; } FieldInfo field = ((object)player).GetType().GetField("m_stamina", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object value = field.GetValue(player); if (value is float) { float num = (float)value; field.SetValue(player, Mathf.Max(0f, num - amount)); } } } internal static bool EnsureReady() { //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Expected O, but got Unknown try { if ((Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("CapeFracturedBreath"); if ((Object)(object)itemPrefab != (Object)null) { CapePrefab = itemPrefab; CapeItemDrop = itemPrefab.GetComponent(); if ((Object)(object)CapeItemDrop != (Object)null) { SharedData shared = CapeItemDrop.m_itemData.m_shared; ConfigureShared(shared); CapeItemDrop.m_itemData.m_dropPrefab = itemPrefab; shared.m_canBeReparied = true; shared.m_useDurability = true; } HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Stamina cape already in ObjectDB: CapeFracturedBreath"); return (Object)(object)CapeItemDrop != (Object)null; } GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("CapeLox"); if ((Object)(object)itemPrefab2 == (Object)null) { ManualLogSource log = HaldorExpansionPlugin.Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source cape prefab not found: CapeLox"); } return false; } GameObject val = PrefabManager.Instance.CreateClonedPrefab("CapeFracturedBreath", ((Object)itemPrefab2).name); if ((Object)(object)val == (Object)null) { ManualLogSource log2 = HaldorExpansionPlugin.Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Failed to clone stamina cape prefab"); } return false; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = HaldorExpansionPlugin.Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on stamina cape"); } return false; } ConfigureShared(component.m_itemData.m_shared); component.m_itemData.m_dropPrefab = val; ItemManager.Instance.AddItem(new CustomItem(val, true)); CapePrefab = val; CapeItemDrop = component; HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Stamina cape registered: CapeFracturedBreath (source=" + ((Object)itemPrefab2).name + ")"); return true; } catch (Exception arg) { ManualLogSource log4 = HaldorExpansionPlugin.Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] EnsureReady stamina cape error: {arg}"); } return false; } } internal static void RegisterRecipe() { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown try { string text = "Recipe_CapeFracturedBreath"; if (ItemManager.Instance.GetRecipe(text) != null) { HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Recipe already registered: " + text); return; } ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("CapeLox") : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = HaldorExpansionPlugin.Log; if (log != null) { log.LogWarning((object)"[HaldorExpansion] Source cape prefab not found for recipe"); } return; } ItemDrop component = val.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log2 = HaldorExpansionPlugin.Log; if (log2 != null) { log2.LogWarning((object)"[HaldorExpansion] Source cape ItemDrop not found for recipe"); } return; } Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); if ((Object)(object)recipe == (Object)null) { ManualLogSource log3 = HaldorExpansionPlugin.Log; if (log3 != null) { log3.LogWarning((object)"[HaldorExpansion] Source cape recipe not found"); } return; } string craftingStation = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : null); string repairStation = "forge"; RecipeConfig val2 = new RecipeConfig { Name = text, Item = "CapeFracturedBreath", Amount = 1, CraftingStation = craftingStation, RepairStation = repairStation, MinStationLevel = 1, Enabled = false, Requirements = Array.Empty() }; ItemManager.Instance.AddRecipe(new CustomRecipe(val2)); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Registered recipe via Jotunn for CapeFracturedBreath"); } catch (Exception arg) { ManualLogSource log4 = HaldorExpansionPlugin.Log; if (log4 != null) { log4.LogError((object)$"[HaldorExpansion] RegisterRecipe stamina cape error: {arg}"); } } } internal static bool FixItemsInInventory(Player player) { try { if ((Object)(object)player == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("CapeFracturedBreath"); if ((Object)(object)itemPrefab == (Object)null) { return false; } ItemDrop component = itemPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return false; } bool flag = false; bool flag2 = false; List allItems = inventory.GetAllItems(); if (allItems == null) { return false; } foreach (ItemData item in allItems) { if (item != null && item.m_shared != null && !(item.m_shared.m_name != "$item_cape_fracturedbreath")) { flag = true; int num = Mathf.Max(1, item.m_quality); float durability = item.m_durability; bool equipped = item.m_equipped; int variant = item.m_variant; long crafterID = item.m_crafterID; string crafterName = item.m_crafterName; item.m_dropPrefab = itemPrefab; item.m_shared = component.m_itemData.m_shared; item.m_quality = Mathf.Clamp(num, 1, Mathf.Max(1, item.m_shared.m_maxQuality)); item.m_variant = variant; item.m_crafterID = crafterID; item.m_crafterName = crafterName; item.m_equipped = equipped; float num2 = item.m_shared.m_maxDurability + item.m_shared.m_durabilityPerLevel * (float)(item.m_quality - 1); item.m_durability = Mathf.Clamp(durability, 0f, Mathf.Max(1f, num2)); item.m_shared.m_canBeReparied = true; item.m_shared.m_useDurability = true; flag2 = true; } } if (flag) { Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData); string[] obj = new string[10] { "[HaldorExpansion] Stamina cape inventory rebind checked: ", $"changed={flag2}, ", "recipe=", ((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null", ", craft=", null, null, null, null, null }; object obj2; if (!((Object)(object)recipe != (Object)null)) { obj2 = "null"; } else { CraftingStation craftingStation = recipe.m_craftingStation; obj2 = ((craftingStation != null) ? ((Object)craftingStation).name : null); } obj[5] = (string)obj2; obj[6] = ", repair="; object obj3; if (!((Object)(object)recipe != (Object)null)) { obj3 = "null"; } else { CraftingStation repairStation = recipe.m_repairStation; obj3 = ((repairStation != null) ? ((Object)repairStation).name : null); } obj[7] = (string)obj3; obj[8] = ", dropPrefab="; obj[9] = ((Object)itemPrefab).name; HaldorExpansionPlugin.DebugLog(string.Concat(obj)); } return flag; } catch (Exception arg) { ManualLogSource log = HaldorExpansionPlugin.Log; if (log != null) { log.LogError((object)$"[HaldorExpansion] FixItemsInInventory stamina cape error: {arg}"); } return false; } } private static void ConfigureShared(SharedData shared) { if (shared == null) { return; } shared.m_name = "$item_cape_fracturedbreath"; shared.m_description = "$item_cape_fracturedbreath_desc"; shared.m_armor = 4f; shared.m_maxQuality = 1; shared.m_weight = 1f; shared.m_maxDurability = 1800f; shared.m_durabilityPerLevel = 0f; shared.m_value = 0; shared.m_canBeReparied = true; shared.m_useDurability = true; shared.m_setName = ""; shared.m_setSize = 0; shared.m_setStatusEffect = null; if (shared.m_damageModifiers != null) { shared.m_damageModifiers.RemoveAll((DamageModPair mod) => (int)mod.m_type == 64); } ApplyVisualEffect(shared); } private static void ApplyVisualEffect(SharedData shared) { if (shared != null) { SE_Stats val = ScriptableObject.CreateInstance(); ((Object)val).name = "SE_FracturedBreathCapeVisual"; SetFieldIfExists(val, "m_name", "$se_cape_fracturedbreath"); SetFieldIfExists(val, "m_tooltip", "$se_cape_fracturedbreath_desc"); shared.m_equipStatusEffect = (StatusEffect)(object)val; } } private static void SetFieldIfExists(object obj, string fieldName, object value) { FieldInfo field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, value); } } private static float InvokeFloatMethod(object target, string methodName) { if (target == null) { return 0f; } Type type = target.GetType(); while (type != null) { MethodInfo method = type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null && method.ReturnType == typeof(float)) { object obj = method.Invoke(target, null); if (obj is float) { return (float)obj; } } type = type.BaseType; } return 0f; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class StaminaCapeObjectDBAwakePatch { [CompilerGenerated] private sealed class d__1 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = (object)new WaitUntil((Func)(() => (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items != null && ObjectDB.instance.m_items.Count > 0 && ObjectDB.instance.m_recipes != null && ObjectDB.instance.m_recipes.Count > 0)); <>1__state = 2; return true; case 2: <>1__state = -1; StaminaCapeFeature.EnsureReady(); StaminaCapeFeature.RegisterRecipe(); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static void Postfix() { StaminaCapeFeature.ResetCaches(); if ((Object)(object)HaldorExpansionPlugin.Instance != (Object)null) { ((MonoBehaviour)HaldorExpansionPlugin.Instance).StartCoroutine(DelayedEnsure()); } } [IteratorStateMachine(typeof(d__1))] private static IEnumerator DelayedEnsure() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0); } } [HarmonyPatch(typeof(Player), "Update")] internal static class StaminaCapeInventoryFixPatch { private static readonly HashSet ProcessedPlayers = new HashSet(); private static void Postfix(Player __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)ObjectDB.instance == (Object)null)) { int instanceID = ((Object)__instance).GetInstanceID(); if (!ProcessedPlayers.Contains(instanceID)) { StaminaCapeFeature.FixItemsInInventory(__instance); ProcessedPlayers.Add(instanceID); } } } } [HarmonyPatch] internal static class HaldorStaminaCapeShopPatch { private static MethodBase TargetMethod() { MethodInfo methodInfo = null; MethodInfo[] methods = typeof(Trader).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "Interact")) { if (methodInfo == null) { methodInfo = methodInfo2; } ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length >= 1 && typeof(Humanoid).IsAssignableFrom(parameters[0].ParameterType)) { return methodInfo2; } } } return methodInfo; } private static void Prefix(Trader __instance) { if (IsHaldor(__instance)) { if (__instance.m_items == null) { __instance.m_items = new List(); } StaminaCapeFeature.EnsureReady(); TryAddCape(__instance); } } private static void TryAddCape(Trader trader) { ItemDrop capeItemDrop = StaminaCapeFeature.CapeItemDrop; if (!((Object)(object)capeItemDrop == (Object)null) && !HasPrefab(trader, "CapeFracturedBreath")) { AddTradeItem(trader, capeItemDrop, 2500, 1); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Stamina cape added to Haldor shop"); } } private static bool IsHaldor(Trader trader) { if ((Object)(object)trader != (Object)null && (Object)(object)((Component)trader).gameObject != (Object)null) { return ((Object)((Component)trader).gameObject).name.StartsWith("Haldor"); } return false; } private static bool HasPrefab(Trader trader, string prefabName) { foreach (TradeItem item in trader.m_items) { if ((Object)(object)item.m_prefab != (Object)null && ((Object)item.m_prefab).name == prefabName) { return true; } } return false; } private static void AddTradeItem(Trader trader, ItemDrop itemDrop, int price, int stack) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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_0025: Expected O, but got Unknown trader.m_items.Add(new TradeItem { m_prefab = itemDrop, m_price = price, m_stack = stack }); } } } namespace HaldorExpansion.Patches { [HarmonyPatch(typeof(Player), "GetAdrenaline")] internal static class CestusAdrenalineValuePatch { private static void Postfix(Player __instance, ref float __result) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { if (HaldorExpansionPlugin.ShouldForceHideCestusHudBar(__instance)) { __result = 0f; } else if (HaldorExpansionPlugin.ShouldMirrorCestusToAdrenaline(__instance)) { __result = HaldorExpansionPlugin.GetCestusCharge(__instance); } } } } [HarmonyPatch(typeof(Player), "GetMaxAdrenaline")] internal static class CestusAdrenalineMaxPatch { private static void Postfix(Player __instance, ref float __result) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { if (HaldorExpansionPlugin.ShouldForceHideCestusHudBar(__instance)) { __result = 0f; } else if (HaldorExpansionPlugin.ShouldMirrorCestusToAdrenaline(__instance)) { __result = 100f; } } } } [HarmonyPatch(typeof(Player), "Update")] internal static class CestusActivationInputPatch { private static readonly Type ZInputType = AccessTools.TypeByName("ZInput"); private static readonly MethodInfo GetButtonDownMethod = ((ZInputType != null) ? AccessTools.Method(ZInputType, "GetButtonDown", new Type[1] { typeof(string) }, (Type[])null) : null); private static bool IsCestusActivationPressed() { if (GetButtonDownMethod == null) { return false; } if (HaldorExpansionPlugin.CestusAbilityButton == null) { return false; } object obj = GetButtonDownMethod.Invoke(null, new object[1] { HaldorExpansionPlugin.CestusAbilityButton.Name }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static bool ShouldPoll(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer) { return false; } if (HaldorExpansionPlugin.CestusAbilityButton == null) { return false; } if (!HaldorExpansionPlugin.HasCestusEquipped(player)) { return false; } if (!HaldorExpansionPlugin.HasFullCestusCharge(player)) { return false; } if (HaldorExpansionPlugin.IsCestusCasting(player)) { return false; } if (HaldorExpansionPlugin.IsCestusShieldActive(player)) { return false; } return true; } private static void Prefix(Player __instance) { HaldorExpansionPlugin.UpdateCestusLocalEquipState(__instance); HaldorExpansionPlugin.UpdateCestusLocalDamageCharge(__instance); if (ShouldPoll(__instance) && IsCestusActivationPressed() && HaldorExpansionPlugin.TryActivateCestusShield(__instance)) { HaldorExpansionPlugin.ClearHumanoidBlockState((Humanoid)(object)__instance); } } } [HarmonyPatch(typeof(Humanoid), "StartAttack")] internal static class CestusAttackBlockDuringCastPatch { private static bool Prefix(Humanoid __instance, ref bool __result) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val == null) { return true; } if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer) { return true; } if (!HaldorExpansionPlugin.IsCestusInputLocked(val)) { return true; } HaldorExpansionPlugin.ClearHumanoidAttackState((Humanoid)(object)val); HaldorExpansionPlugin.ClearHumanoidBlockState((Humanoid)(object)val); __result = false; return false; } } [HarmonyPatch(typeof(Player), "SetControls")] internal static class CestusCastInputLockPatch { private static void Prefix(Player __instance, ref Vector3 movedir, ref bool attack, ref bool secondaryAttack, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && HaldorExpansionPlugin.IsCestusInputLocked(__instance)) { movedir = Vector3.zero; attack = false; secondaryAttack = false; block = false; blockHold = false; jump = false; crouch = false; } } } [HarmonyPatch(typeof(Hud), "Update")] internal static class CestusShieldHudPatch { private static void Postfix(Hud __instance) { if (!((Object)(object)__instance == (Object)null)) { Player localPlayer = Player.m_localPlayer; HaldorExpansionPlugin.UpdateCestusShieldOverlay(__instance, localPlayer); } } } [HarmonyPatch(typeof(HitData), "ApplyArmor", new Type[] { typeof(float) })] internal static class DelayedDoomApplyArmorPatch { private static void Postfix(HitData __instance) { DelayedDoomDamagePatch.DamageContext damageContext = DelayedDoomDamagePatch.PeekContext(); if (damageContext == null || !damageContext.Enabled || damageContext.ArmorProcessed) { return; } damageContext.ArmorProcessed = true; HaldorExpansionPlugin.DebugLog("[HaldorExpansion] ApplyArmor enter: " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(damageContext.Player) + ", " + $"Cestus={damageContext.HasCestus}"); float hitTotalDamage = HaldorExpansionPlugin.GetHitTotalDamage(__instance); if (hitTotalDamage <= 0f) { return; } HaldorExpansionPlugin.DebugLog("[HaldorExpansion] ApplyArmor post-processing: " + $"hasCestus={damageContext.HasCestus}, " + $"postArmor={hitTotalDamage:0.##}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(damageContext.Player)); float hitTotalDamage2 = HaldorExpansionPlugin.GetHitTotalDamage(__instance); if (damageContext.HasCestus && (Object)(object)damageContext.Player != (Object)null && hitTotalDamage2 > 0f) { float num = (damageContext.CestusShieldAbsorbedInApplyArmor = HaldorExpansionPlugin.AbsorbCestusShield(damageContext.Player, hitTotalDamage2)); if (num > 0f) { float num2 = Mathf.Max(0f, hitTotalDamage2 - num); float num3 = ((hitTotalDamage2 > 0f) ? (num2 / hitTotalDamage2) : 0f); ((DamageTypes)(ref __instance.m_damage)).Modify(num3); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Cestus shield pre-armor absorb applied: " + $"incoming={hitTotalDamage2:0.##}, " + $"absorbed={num:0.##}, " + $"remaining={num2:0.##}, " + $"scale={num3:0.###}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(damageContext.Player)); } } } } [HarmonyPatch(typeof(Character), "ApplyDamage")] [HarmonyPriority(0)] internal static class DelayedDoomApplyDamagePatch { private sealed class ApplyDamageState { public Player Player; public bool Armed; public bool Triggered; public float BeforeHealth; public float MaxHealth; public float IncomingDamage; public float ImmediateDamage; public float DelayedDamageTotal; } private static void Prefix(Character __instance, HitData __0, ref ApplyDamageState __state) { __state = null; Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null) && __0 != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !HaldorExpansionPlugin.IsDelayedDoomInternalDamage((Character)(object)val) && HaldorExpansionPlugin.HasDelayedDoomEquipped(val)) { float characterHealth = HaldorExpansionPlugin.GetCharacterHealth((Character)(object)val); float characterMaxHealth = HaldorExpansionPlugin.GetCharacterMaxHealth((Character)(object)val); float hitTotalDamage = HaldorExpansionPlugin.GetHitTotalDamage(__0); float num = characterMaxHealth * 0.5f; __state = new ApplyDamageState { Player = val, Armed = true, BeforeHealth = characterHealth, MaxHealth = characterMaxHealth, IncomingDamage = hitTotalDamage }; HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Delayed Doom ApplyDamage prefix: " + $"incoming={hitTotalDamage:0.##}, " + $"before={characterHealth:0.##}, " + $"max={characterMaxHealth:0.##}, " + $"threshold={num:0.##}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(val)); if (!(characterHealth <= 0f) && !(characterMaxHealth <= 0f) && !(hitTotalDamage <= 0f) && !(hitTotalDamage <= num)) { float num2 = hitTotalDamage * 0.5f; float num3 = hitTotalDamage * 0.7f; float num4 = num2 / hitTotalDamage; ((DamageTypes)(ref __0.m_damage)).Modify(num4); __state.Triggered = true; __state.ImmediateDamage = num2; __state.DelayedDamageTotal = num3; HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Delayed Doom ApplyDamage modified hit: " + $"incoming={hitTotalDamage:0.##}, " + $"immediate={num2:0.##}, " + $"delayed={num3:0.##}, " + $"scale={num4:0.###}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(val)); } } } private static void Postfix(Character __instance, HitData __0, ApplyDamageState __state) { if (__state == null || !__state.Armed || !__state.Triggered) { return; } Player player = __state.Player; if (!((Object)(object)player == (Object)null)) { float characterHealth = HaldorExpansionPlugin.GetCharacterHealth((Character)(object)player); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Delayed Doom ApplyDamage postfix: " + $"incoming={__state.IncomingDamage:0.##}, " + $"immediate={__state.ImmediateDamage:0.##}, " + $"delayed={__state.DelayedDamageTotal:0.##}, " + $"before={__state.BeforeHealth:0.##}, " + $"after={characterHealth:0.##}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(player)); if (((Character)player).IsDead() || characterHealth <= 0f) { HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Delayed Doom debt skipped because player died from immediate hit: " + $"incoming={__state.IncomingDamage:0.##}, " + $"immediate={__state.ImmediateDamage:0.##}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(player)); } else { HaldorExpansionPlugin.AddDelayedDoomDebt(player, __state.DelayedDamageTotal); } } } } [HarmonyPatch(typeof(Character), "ApplyDamage")] [HarmonyPriority(800)] internal static class StaminaCapeApplyDamagePatch { private static void Prefix(Character __instance, HitData __0) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)val == (Object)null || __0 == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || HaldorExpansionPlugin.IsDelayedDoomInternalDamage((Character)(object)val) || !StaminaCapeFeature.HasEquipped(val)) { return; } float hitTotalDamage = HaldorExpansionPlugin.GetHitTotalDamage(__0); if (!(hitTotalDamage <= 0f)) { float num = hitTotalDamage * 0.15f; float num2 = num * 1.5f; float num3 = Mathf.Max(0f, StaminaCapeFeature.GetPlayerStamina(val)); float num4 = ((num2 > 0f) ? Mathf.Clamp01(num3 / num2) : 1f); float num5 = num * num4; float num6 = Mathf.Min(num3, num2); float num7 = Mathf.Max(0f, num - num5); float num8 = ((!(num7 > 0.001f)) ? (hitTotalDamage - num5) : ((hitTotalDamage - num5) * 1.3f)); num8 = Mathf.Max(0f, num8); float num9 = ((hitTotalDamage > 0f) ? (num8 / hitTotalDamage) : 1f); ((DamageTypes)(ref __0.m_damage)).Modify(num9); if (num6 > 0.001f) { StaminaCapeFeature.SpendPlayerStamina(val, num6); } HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Stamina cape ApplyDamage: " + $"incoming={hitTotalDamage:0.##}, " + $"redirectTarget={num:0.##}, " + $"staminaCost={num2:0.##}, " + $"staminaBefore={num3:0.##}, " + $"staminaSpent={num6:0.##}, " + $"missingRedirect={num7:0.##}, " + $"hpAfterCape={num8:0.##}, " + $"overflow={((num7 > 0.001f) ? 1 : 0)}, " + $"scale={num9:0.###}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(val)); } } } [HarmonyPatch(typeof(Character), "Damage")] internal static class DelayedDoomDamagePatch { internal sealed class DamageContext { public Player Player; public HitData Hit; public bool Enabled; public bool ArmorProcessed; public bool HasCestus; public float BeforeHealth; public float MaxHealth; public float CestusShieldAbsorbedInApplyArmor; public float CestusShieldCorrectedInPostfix; } private static readonly Stack ContextStack = new Stack(); internal static DamageContext PeekContext() { if (ContextStack.Count <= 0) { return null; } return ContextStack.Peek(); } private static void Prefix(Character __instance, HitData hit) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)val == (Object)null || hit == null) { return; } DamageContext damageContext = new DamageContext { Player = val, Hit = hit }; bool flag = HaldorExpansionPlugin.HasCestusEquipped(val); bool flag2 = HaldorExpansionPlugin.GetCestusEffectiveShieldRemaining(val) > 0.001f; bool flag3 = HaldorExpansionPlugin.ShouldProcessCestusLocally(val) && (flag || flag2); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Damage prefix: " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(val) + ", " + $"CestusDetected={flag}, " + $"SyncedShieldActive={flag2}, " + $"CestusLocalRuntime={flag3}"); bool num = HaldorExpansionPlugin.IsDelayedDoomInternalDamage(__instance); if (num) { HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Damage prefix skip internal Delayed Doom damage: " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(val)); } if (!num && flag3) { float characterHealth = HaldorExpansionPlugin.GetCharacterHealth((Character)(object)val); float characterMaxHealth = HaldorExpansionPlugin.GetCharacterMaxHealth((Character)(object)val); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Damage snapshot: " + $"before={characterHealth:0.##}, " + $"max={characterMaxHealth:0.##}, " + $"shieldActive={(flag2 ? 1 : 0)}"); if (characterHealth > 0f && characterMaxHealth > 0f) { damageContext.Enabled = true; damageContext.BeforeHealth = characterHealth; damageContext.MaxHealth = characterMaxHealth; damageContext.HasCestus = flag3; } } ContextStack.Push(damageContext); } private static void Postfix(Character __instance, HitData hit) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)val == (Object)null || hit == null || ContextStack.Count == 0) { return; } DamageContext damageContext = ContextStack.Pop(); if (!damageContext.Enabled) { return; } float characterHealth = HaldorExpansionPlugin.GetCharacterHealth((Character)(object)val); if (damageContext.HasCestus && damageContext.CestusShieldAbsorbedInApplyArmor <= 0.001f) { damageContext.CestusShieldCorrectedInPostfix = HaldorExpansionPlugin.CorrectCestusShieldAfterDamage(val, damageContext.BeforeHealth, characterHealth); if (damageContext.CestusShieldCorrectedInPostfix > 0.001f) { characterHealth = HaldorExpansionPlugin.GetCharacterHealth((Character)(object)val); } } if (damageContext.HasCestus) { HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Damage postfix after cestus handling: " + $"before={damageContext.BeforeHealth:0.##}, " + $"after={characterHealth:0.##}, " + $"cestusPreAbsorb={damageContext.CestusShieldAbsorbedInApplyArmor:0.##}, " + $"cestusPostfixCorrect={damageContext.CestusShieldCorrectedInPostfix:0.##}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(val)); } } } [HarmonyPatch(typeof(Player), "UpdateFood", new Type[] { typeof(float), typeof(bool) })] internal static class DelayedDoomFoodRegenPatch { private sealed class FoodRegenSnapshot { public Player Player; public bool Enabled; public float BeforeHealth; public float BaseFoodRegen; public float Dt; public bool ForceUpdate; } private static readonly Stack Pending = new Stack(); private static void Prefix(Player __instance, float dt, bool forceUpdate) { FoodRegenSnapshot foodRegenSnapshot = new FoodRegenSnapshot { Player = __instance, Dt = dt, ForceUpdate = forceUpdate, Enabled = false, BeforeHealth = 0f, BaseFoodRegen = 0f }; if ((Object)(object)__instance != (Object)null && HaldorExpansionPlugin.HasDelayedDoomEquipped(__instance) && dt > 0f && !forceUpdate) { foodRegenSnapshot.Enabled = true; foodRegenSnapshot.BeforeHealth = HaldorExpansionPlugin.GetCharacterHealth((Character)(object)__instance); foodRegenSnapshot.BaseFoodRegen = HaldorExpansionPlugin.GetPlayerBaseFoodRegen(__instance); } Pending.Push(foodRegenSnapshot); } private static void Postfix(Player __instance, float dt, bool forceUpdate) { if (Pending.Count == 0) { return; } FoodRegenSnapshot foodRegenSnapshot = Pending.Pop(); if (!foodRegenSnapshot.Enabled || (Object)(object)__instance == (Object)null || foodRegenSnapshot.BaseFoodRegen <= 0.001f) { return; } float characterHealth = HaldorExpansionPlugin.GetCharacterHealth((Character)(object)__instance); float num = characterHealth - foodRegenSnapshot.BeforeHealth; if (num <= 0.001f) { return; } float num2 = num / foodRegenSnapshot.BaseFoodRegen; num2 = Mathf.Clamp(num2, 0f, 10f); float num3 = 5f * num2; if (!(num3 <= 0.001f)) { float num4 = Mathf.Min(HaldorExpansionPlugin.GetCharacterMaxHealth((Character)(object)__instance), characterHealth + num3); if (!(num4 <= characterHealth + 0.001f)) { HaldorExpansionPlugin.SetCharacterHealth((Character)(object)__instance, num4); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Food regen bonus applied: " + $"baseFoodRegen={foodRegenSnapshot.BaseFoodRegen:0.##}, " + $"foodHeal={num:0.##}, " + $"scale={num2:0.##}, " + $"bonus={num3:0.##}, " + $"after={characterHealth:0.##}, " + $"new={num4:0.##}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(__instance)); } } } } [HarmonyPatch(typeof(Player), "Update")] internal static class DelayedDoomInventoryFixPatch { private static readonly HashSet ProcessedPlayers = new HashSet(); private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return; } int instanceID = ((Object)__instance).GetInstanceID(); if (ProcessedPlayers.Contains(instanceID)) { return; } foreach (ItemData allItem in ((Humanoid)__instance).GetInventory().GetAllItems()) { if (allItem != null && allItem.m_shared != null) { Recipe recipe = ObjectDB.instance.GetRecipe(allItem); string[] obj = new string[10] { "[RepairDebug] item=", allItem.m_shared.m_name, ", recipe=", ((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null", ", craft=", null, null, null, null, null }; object obj2; if (!((Object)(object)recipe != (Object)null)) { obj2 = "null"; } else { CraftingStation craftingStation = recipe.m_craftingStation; obj2 = ((craftingStation != null) ? ((Object)craftingStation).name : null); } obj[5] = (string)obj2; obj[6] = ", repair="; object obj3; if (!((Object)(object)recipe != (Object)null)) { obj3 = "null"; } else { CraftingStation repairStation = recipe.m_repairStation; obj3 = ((repairStation != null) ? ((Object)repairStation).name : null); } obj[7] = (string)obj3; obj[8] = ", "; obj[9] = $"durability={allItem.m_durability:0.##}/{allItem.GetMaxDurability():0.##}"; HaldorExpansionPlugin.DebugLog(string.Concat(obj)); } } HaldorExpansionPlugin.FixDelayedDoomItemsInInventory(__instance); HaldorExpansionPlugin.FixCestusItemsInInventory(__instance); ProcessedPlayers.Add(instanceID); } } [HarmonyPatch] internal static class HaldorShopExpansionPatch { private sealed class ShopItemDefinition { public string PrefabName { get; private set; } public int Price { get; private set; } public int Stack { get; private set; } public bool UseMaxStack { get; private set; } public ShopItemDefinition(string prefabName, int price, int stack, bool useMaxStack = false) { PrefabName = prefabName; Price = price; Stack = stack; UseMaxStack = useMaxStack; } } private static readonly ShopItemDefinition[] ShopItems = new ShopItemDefinition[24] { new ShopItemDefinition("Iron", 225, 30), new ShopItemDefinition("Copper", 310, 30), new ShopItemDefinition("Tin", 180, 30), new ShopItemDefinition("Silver", 550, 30), new ShopItemDefinition("Flametal", 800, 30), new ShopItemDefinition("Stone", 20, 1, useMaxStack: true), new ShopItemDefinition("Grausten", 25, 1, useMaxStack: true), new ShopItemDefinition("BlackMarble", 40, 1, useMaxStack: true), new ShopItemDefinition("Wood", 10, 1, useMaxStack: true), new ShopItemDefinition("RoundLog", 10, 1, useMaxStack: true), new ShopItemDefinition("ElderBark", 30, 1, useMaxStack: true), new ShopItemDefinition("FineWood", 150, 1, useMaxStack: true), new ShopItemDefinition("YggdrasilWood", 250, 1, useMaxStack: true), new ShopItemDefinition("Ashwood", 30, 1, useMaxStack: true), new ShopItemDefinition("Blackwood", 100, 1, useMaxStack: true), new ShopItemDefinition("CharredWood", 50, 1, useMaxStack: true), new ShopItemDefinition("JuteRed", 250, 1, useMaxStack: true), new ShopItemDefinition("JuteBlue", 250, 1, useMaxStack: true), new ShopItemDefinition("Resin", 100, 1, useMaxStack: true), new ShopItemDefinition("Feathers", 150, 100), new ShopItemDefinition("GreydwarfEye", 200, 1, useMaxStack: true), new ShopItemDefinition("Tar", 250, 1, useMaxStack: true), new ShopItemDefinition("Crystal", 250, 1, useMaxStack: true), new ShopItemDefinition("Needle", 250, 1, useMaxStack: true) }; private static MethodBase TargetMethod() { MethodInfo methodInfo = null; MethodInfo[] methods = typeof(Trader).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "Interact")) { if (methodInfo == null) { methodInfo = methodInfo2; } ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length >= 1 && typeof(Humanoid).IsAssignableFrom(parameters[0].ParameterType)) { return methodInfo2; } } } return methodInfo; } private static void Prefix(Trader __instance) { if (IsHaldor(__instance)) { if (__instance.m_items == null) { __instance.m_items = new List(); } TryAddConfiguredItems(__instance); TryAddRing(__instance); TryAddWoundedBeastCape(__instance); TryAddDelayedDoomChest(__instance); TryAddPitKingChest(__instance); TryAddCestus(__instance); TryAddShadowCrossbow(__instance); } } private static void TryAddWoundedBeastCape(Trader trader) { HaldorExpansionPlugin.EnsureWoundedBeastCapeReady(); ItemDrop woundedBeastCapeItemDrop = HaldorExpansionPlugin.WoundedBeastCapeItemDrop; if (!((Object)(object)woundedBeastCapeItemDrop == (Object)null) && !HasPrefab(trader, "CapeWoundedBeast")) { AddTradeItem(trader, woundedBeastCapeItemDrop, 2500, 1); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Wounded Beast cape added to Haldor shop"); } } private static void TryAddShadowCrossbow(Trader trader) { HaldorExpansionPlugin.EnsureShadowCrossbowReady(); ItemDrop shadowCrossbowItemDrop = HaldorExpansionPlugin.ShadowCrossbowItemDrop; if (!((Object)(object)shadowCrossbowItemDrop == (Object)null) && !HasPrefab(trader, "CrossbowSilentHunt")) { AddTradeItem(trader, shadowCrossbowItemDrop, 2500, 1); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Shadow crossbow added to Haldor shop"); } } private static void TryAddPitKingChest(Trader trader) { HaldorExpansionPlugin.EnsurePitKingChestReady(); ItemDrop pitKingChestItemDrop = HaldorExpansionPlugin.PitKingChestItemDrop; if (!((Object)(object)pitKingChestItemDrop == (Object)null) && !HasPrefab(trader, "ArmorPitKingChest")) { AddTradeItem(trader, pitKingChestItemDrop, 4000, 1); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Pit King chest added to Haldor shop"); } } private static void TryAddCestus(Trader trader) { HaldorExpansionPlugin.EnsureCestusReady(); ItemDrop gritCestusItemDrop = HaldorExpansionPlugin.GritCestusItemDrop; if (!((Object)(object)gritCestusItemDrop == (Object)null) && !HasPrefab(trader, "Bone_crushers")) { AddTradeItem(trader, gritCestusItemDrop, 2500, 1); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Cestus added to Haldor shop"); } } private static void TryAddDelayedDoomChest(Trader trader) { HaldorExpansionPlugin.EnsureDelayedDoomChestReady(); ItemDrop delayedDoomChestItemDrop = HaldorExpansionPlugin.DelayedDoomChestItemDrop; if (!((Object)(object)delayedDoomChestItemDrop == (Object)null) && !HasPrefab(trader, "ArmorDelayedDoomChest")) { AddTradeItem(trader, delayedDoomChestItemDrop, 4000, 1); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Delayed Doom chest added to Haldor shop"); } } private static bool IsHaldor(Trader trader) { if ((Object)(object)trader != (Object)null && (Object)(object)((Component)trader).gameObject != (Object)null) { return ((Object)((Component)trader).gameObject).name.StartsWith("Haldor"); } return false; } private static void TryAddConfiguredItems(Trader trader) { if (!((Object)(object)ObjectDB.instance == (Object)null)) { ShopItemDefinition[] shopItems = ShopItems; foreach (ShopItemDefinition item in shopItems) { TryAddConfiguredItem(trader, item); } } } private static void TryAddConfiguredItem(Trader trader, ShopItemDefinition item) { if ((Object)(object)ObjectDB.instance == (Object)null || HasPrefab(trader, item.PrefabName)) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.PrefabName); if ((Object)(object)itemPrefab == (Object)null) { HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Prefab not found: " + item.PrefabName); return; } ItemDrop component = itemPrefab.GetComponent(); if ((Object)(object)component == (Object)null) { ManualLogSource log = HaldorExpansionPlugin.Log; if (log != null) { log.LogWarning((object)("[HaldorExpansion] ItemDrop missing on prefab: " + item.PrefabName)); } return; } int stack = ResolveStack(component, item); AddTradeItem(trader, component, item.Price, stack); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Added " + item.PrefabName + " x" + stack + " for " + item.Price + " coins"); } private static int ResolveStack(ItemDrop itemDrop, ShopItemDefinition item) { if (!item.UseMaxStack) { return item.Stack; } if (itemDrop.m_itemData != null && itemDrop.m_itemData.m_shared != null) { int maxStackSize = itemDrop.m_itemData.m_shared.m_maxStackSize; if (maxStackSize > 0) { return maxStackSize; } } return 1; } private static void TryAddRing(Trader trader) { HaldorExpansionPlugin.EnsureRingReady(); ItemDrop ringItemDrop = HaldorExpansionPlugin.RingItemDrop; if (!((Object)(object)ringItemDrop == (Object)null) && !HasPrefab(trader, "BrisingamenRing")) { AddTradeItem(trader, ringItemDrop, 1600, 1); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Ring added to Haldor shop"); } } private static bool HasPrefab(Trader trader, string prefabName) { foreach (TradeItem item in trader.m_items) { if ((Object)(object)item.m_prefab != (Object)null && ((Object)item.m_prefab).name == prefabName) { return true; } } return false; } private static void AddTradeItem(Trader trader, ItemDrop itemDrop, int price, int stack) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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_0025: Expected O, but got Unknown trader.m_items.Add(new TradeItem { m_prefab = itemDrop, m_price = price, m_stack = stack }); } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDB_Awake_Patch { [CompilerGenerated] private sealed class d__1 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = (object)new WaitUntil((Func)(() => (Object)(object)ObjectDB.instance != (Object)null && ObjectDB.instance.m_items != null && ObjectDB.instance.m_items.Count > 0 && ObjectDB.instance.m_recipes != null && ObjectDB.instance.m_recipes.Count > 0)); <>1__state = 2; return true; case 2: <>1__state = -1; HaldorExpansionPlugin.EnsureRingReady(); HaldorExpansionPlugin.EnsureDelayedDoomChestReady(); HaldorExpansionPlugin.EnsurePitKingChestReady(); HaldorExpansionPlugin.EnsureCestusReady(); HaldorExpansionPlugin.EnsureShadowCrossbowReady(); HaldorExpansionPlugin.EnsureWoundedBeastCapeReady(); HaldorExpansionPlugin.RegisterShadowCrossbowRecipe(); HaldorExpansionPlugin.RegisterCestusRecipe(); HaldorExpansionPlugin.RegisterDelayedDoomChestRecipe(); HaldorExpansionPlugin.RegisterPitKingChestRecipe(); HaldorExpansionPlugin.RegisterWoundedBeastCapeRecipe(); HaldorExpansionPlugin.ApplyTrophyValues(ObjectDB.instance); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static void Postfix() { HaldorExpansionPlugin.ResetItemCaches(); if ((Object)(object)HaldorExpansionPlugin.Instance != (Object)null) { ((MonoBehaviour)HaldorExpansionPlugin.Instance).StartCoroutine(DelayedEnsure()); } } [IteratorStateMachine(typeof(d__1))] private static IEnumerator DelayedEnsure() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0); } } [HarmonyPatch(typeof(Character), "ApplyDamage")] [HarmonyPriority(800)] internal static class WoundedBeastCapeApplyDamagePatch { private static void Prefix(Character __instance, HitData __0) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (!((Object)(object)val == (Object)null) && __0 != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && !HaldorExpansionPlugin.IsDelayedDoomInternalDamage((Character)(object)val) && HaldorExpansionPlugin.HasWoundedBeastCapeEquipped(val)) { float hitTotalDamage = HaldorExpansionPlugin.GetHitTotalDamage(__0); if (!(hitTotalDamage <= 0f)) { ((DamageTypes)(ref __0.m_damage)).Modify(1.15f); float hitTotalDamage2 = HaldorExpansionPlugin.GetHitTotalDamage(__0); HaldorExpansionPlugin.DebugLog("[HaldorExpansion] Wounded Beast ApplyDamage penalty: " + $"before={hitTotalDamage:0.##}, " + $"after={hitTotalDamage2:0.##}, " + $"mult={1.15f:0.##}, " + HaldorExpansionPlugin.GetCestusDebugPlayerTag(val)); } } } } }