private void StaggerAttacker(Character self, Animator m_animator, Character _dealerChar)
        {
            String m_nameLocKey = (String)At.GetValue(typeof(Character), self, "m_nameLocKey");
            String m_name       = (String)At.GetValue(typeof(Character), self, "m_name");

            // Debug.Log(self.Name + " blocked the attack. Shield stability: " + m_shieldStability);
            At.SetValue(Character.HurtType.NONE, typeof(Character), self, "m_hurtType");
            if (self.InLocomotion)
            {
                m_animator.SetTrigger("BlockHit");
            }

            if ((bool)CombatOverhaul.config.GetValue(Settings.Blocking_Staggers_Attacker))
            {
                // Debug.Log("autoknocking " + _dealerChar.Name);
                if (_dealerChar.CurrentWeapon.Type != Weapon.WeaponType.Bow)
                {
                    bool ShouldBounce = true;

                    if (!(bool)CombatOverhaul.config.GetValue(Settings.BossShieldBounce))
                    {
                        ShouldBounce = (int)EnemyClass.getEnemyLevel(_dealerChar) < (int)EnemyLevel.MINIBOSS;
                    }
                    if ((bool)CombatOverhaul.config.GetValue(Settings.OneHBounceOnly))
                    {
                        if (_dealerChar.CurrentWeapon.TwoHanded && EnemyClass.getEnemyLevel(_dealerChar) != EnemyLevel.WEAKLING)
                        {
                            ShouldBounce = false; //2h weapons should not bounce off shields
                        }
                    }

                    if (ShouldBounce)
                    {
                        _dealerChar.AutoKnock(false, new Vector3(0, 0, 0));
                    }
                }
            }
        }
            public static bool Prefix(Character __instance, float _knockValue, float _angle, bool _block, Character _dealerChar)
            {
                var self  = __instance;
                var _base = self as Photon.MonoBehaviour;

                if (At.GetValue(typeof(Character), self, "m_impactImmune") is bool m_impactImmune &&
                    At.GetValue(typeof(Character), self, "m_shieldStability") is float m_shieldStability &&
                    At.GetValue(typeof(Character), self, "m_stability") is float m_stability &&
                    At.GetValue(typeof(Character), self, "m_knockbackCount") is float m_knockbackCount &&
                    At.GetValue(typeof(Character), self, "m_knockHurtAllowed") is bool m_knockHurtAllowed &&
                    At.GetValue(typeof(Character), self, "m_currentlyChargingAttack") is bool m_currentlyChargingAttack &&
                    At.GetValue(typeof(Character), self, "m_animator") is Animator m_animator)
                {
                    String m_nameLocKey = (String)At.GetValue(typeof(Character), self, "m_nameLocKey");
                    String m_name       = (String)At.GetValue(typeof(Character), self, "m_name");

                    // Begin actual stability hit function
                    var hit = _knockValue;
                    if (hit < 0)
                    {
                        hit = 0;
                    }

                    if (!m_impactImmune && hit > 0f)
                    {
                        //Debug.Log("--------- " + self.Name + " ---------");

                        // check stagger immunity dictionary (custom)
                        float lastStagger = -1;
                        if (Instance.LastStaggerTimes.ContainsKey(self.UID))
                        {
                            lastStagger = Instance.LastStaggerTimes[self.UID];
                        }

                        // if you run out of stamina and get hit, you will always get staggered. (unchanged, except to reflect custom stagger threshold)
                        if (self.Stats.CurrentStamina < 1f)
                        {
                            float hitToStagger = m_shieldStability + m_stability - (100 - (float)CombatOverhaul.config.GetValue(Settings.Stagger_Threshold));
                            if (hit < hitToStagger)
                            {
                                hit = hitToStagger;
                            }
                            //Debug.LogError("Stamina autostagger called! hitToStagger: " + hitToStagger + ", hit: " + hit);
                        }

                        At.SetValue(Time.time, typeof(Character), self, "m_timeOfLastStabilityHit");
                        // Debug.Log("Set " + Time.time + " as character's last stability hit");

                        if (self.CharacterCamera != null && hit > 0f)
                        {
                            self.CharacterCamera.Hit(hit * 6f);
                        }

                        // check shield stability if blocking (unchanged)
                        if (_block && m_shieldStability > 0f)
                        {
                            if (hit > m_shieldStability)
                            {
                                var num2 = m_stability - (hit - m_shieldStability);
                                At.SetValue(num2, typeof(Character), self, "m_stability");
                                m_stability = num2;
                            }
                            var num3 = Mathf.Clamp(m_shieldStability - hit, 0f, 50f);
                            At.SetValue(num3, typeof(Character), self, "m_shieldStability");
                            m_shieldStability = num3;
                        }
                        // check non-blocking stability (unchanged)
                        else
                        {
                            var num2 = Mathf.Clamp(m_stability - hit, 0f, 100f);
                            At.SetValue(num2, typeof(Character), self, "m_stability");
                            m_stability = num2;
                        }
                        // if hit takes us below knockdown threshold, or if AI auto-knockdown stagger count was reached...
                        float knockdownThreshold  = (float)CombatOverhaul.config.GetValue(Settings.Knockdown_Threshold);
                        bool  playerHasPoiseBoost = ((bool)CombatOverhaul.config.GetValue(Settings.PlayerPoiseBoost) && EnemyClass.isPlayer(self));

                        if ((bool)CombatOverhaul.config.GetValue(Settings.BossPoise))
                        {
                            if ((int)EnemyClass.getEnemyLevel(self) >= (int)EnemyLevel.MINIBOSS || playerHasPoiseBoost)
                            {
                                knockdownThreshold = -100f; //Bosses MUST be staggered. They can't be knocked down so easily.
                            }
                        }

                        float staggerThreshold = (float)CombatOverhaul.config.GetValue(Settings.Stagger_Threshold);

                        //TODO: Boss stagger thresholds
                        if ((bool)CombatOverhaul.config.GetValue(Settings.BossPoise))
                        {
                            //Debug.Log("Enemy level: " + EnemyClass.getCleanName(self) + " " + EnemyClass.getEnemyLevel(self));
                            //TODO: Test if this is really necessary. Maybe bosses already get good poise.
                            if (EnemyClass.getEnemyLevel(self) == EnemyLevel.BOSS)
                            {
                                staggerThreshold = staggerThreshold / (float)CombatOverhaul.config.GetValue(Settings.BossStaggerMultiplier);
                            }

                            if (EnemyClass.getEnemyLevel(self) == EnemyLevel.MINIBOSS || playerHasPoiseBoost)
                            {
                                staggerThreshold = staggerThreshold / (float)CombatOverhaul.config.GetValue(Settings.MinibossStaggerMultiplier);
                            }
                        }

                        if (m_stability <= knockdownThreshold ||
                            (self.IsAI && m_knockbackCount >= (float)CombatOverhaul.config.GetValue(Settings.Enemy_AutoKD_Count)))
                        {
                            //Debug.LogError("Knockdown! Hit Value: " + _knockValue + ", current stability: " + m_stability);

                            if ((!self.IsAI && _base.photonView.isMine) || (self.IsAI && (_dealerChar == null || _dealerChar.photonView.isMine)))
                            {
                                _base.photonView.RPC("SendKnock", PhotonTargets.All, new object[]
                                {
                                    true,
                                    m_stability
                                });
                            }
                            else
                            {
                                At.Call(typeof(Character), self, "Knock", null, true);
                            }
                            At.SetValue(0f, typeof(Character), self, "m_stability");
                            m_stability = 0f;
                            if (self.IsPhotonPlayerLocal)
                            {
                                self.BlockInput(false);
                            }
                        }
                        // else if hit is a stagger...
                        else if (m_stability <= staggerThreshold && (Time.time - lastStagger > (float)CombatOverhaul.config.GetValue(Settings.Stagger_Immunity_Period)))
                        {
                            // Debug.LogWarning("Stagger! Hit Value: " + _knockValue + ", current stability: " + m_stability);

                            // update Stagger Immunity dictionary
                            if (!Instance.LastStaggerTimes.ContainsKey(self.UID))
                            {
                                Instance.LastStaggerTimes.Add(self.UID, Time.time);
                            }
                            else
                            {
                                Instance.LastStaggerTimes[self.UID] = Time.time;
                            }

                            if ((!self.IsAI && _base.photonView.isMine) || (self.IsAI && (_dealerChar == null || _dealerChar.photonView.isMine)))
                            {
                                if ((bool)CombatOverhaul.config.GetValue(Settings.Poise))
                                {
                                    m_stability = 100f;
                                    At.SetValue(100f, typeof(Character), self, "m_stability");
                                }
                                _base.photonView.RPC("SendKnock", PhotonTargets.All, new object[]
                                {
                                    false,
                                    m_stability
                                });
                            }
                            else
                            {
                                At.Call(typeof(Character), self, "Knock", null, false);
                            }
                            if (self.IsPhotonPlayerLocal && _block)
                            {
                                self.BlockInput(false);
                            }
                            //At.SetValue(100f, typeof(Character), self, "m_stability");
                        }
                        // else if we are not blocking...
                        else if (!_block)
                        {
                            // Debug.Log("Value: " + _knockValue + ", new stability: " + m_stability);
                            if (m_knockHurtAllowed)
                            {
                                At.SetValue(Character.HurtType.Hurt, typeof(Character), self, "m_hurtType");

                                if (m_currentlyChargingAttack)
                                {
                                    self.CancelCharging();
                                }

                                m_animator.SetTrigger("Knockhurt");
                                _base.StopCoroutine("KnockhurtRoutine");

                                MethodInfo  _knockhurtRoutine = self.GetType().GetMethod("KnockhurtRoutine", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
                                IEnumerator _knockEnum        = (IEnumerator)_knockhurtRoutine.Invoke(self, new object[] { hit });
                                _base.StartCoroutine(_knockEnum);
                            }

                            if (m_stability <= (float)CombatOverhaul.config.GetValue(Settings.Stagger_Immunity_Period))
                            {
                                // Debug.LogError(self.Name + " would have staggered. Current delta: " + (Time.time - lastStagger));
                            }
                        }
                        else // hit was blocked and no stagger
                        {
                            Instance.StaggerAttacker(self, m_animator, _dealerChar);
                        }
                        m_animator.SetInteger("KnockAngle", (int)_angle);
                        self.StabilityHitCall?.Invoke();
                    }
                    else if (!m_impactImmune && _block) // hit dealt 0 impact and was blocked
                    {
                        Instance.StaggerAttacker(self, m_animator, _dealerChar);
                    }
                }

                return(false);
            }
            public static bool Prefix(Character __instance)
            {
                var self = __instance;

                if (At.GetValue(typeof(Character), self, "m_stability") is float m_stability &&
                    At.GetValue(typeof(Character), self, "m_timeOfLastStabilityHit") is float m_timeOfLastStabilityHit &&
                    At.GetValue(typeof(Character), self, "m_shieldStability") is float m_shieldStability &&
                    At.GetValue(typeof(Character), self, "m_knockbackCount") is float m_knockbackCount)
                {
                    String m_nameLocKey = (String)At.GetValue(typeof(Character), self, "m_nameLocKey");
                    String m_name       = (String)At.GetValue(typeof(Character), self, "m_name");

                    // ----------- original method, unchanged other than to reflect custom values -------------
                    if ((bool)CombatOverhaul.config.GetValue(Settings.No_Stability_Regen_When_Blocking) && self.Blocking) // no stability regen while blocking! otherwise too op
                    {
                        return(false);
                    }

                    float num = Time.time - m_timeOfLastStabilityHit;
                    if (num > (float)CombatOverhaul.config.GetValue(Settings.Stability_Regen_Delay))
                    {
                        if (m_stability < 100f && !(bool)CombatOverhaul.config.GetValue(Settings.Poise))
                        {
                            var num2 = Mathf.Clamp(m_stability + (self.StabilityRegen * (float)CombatOverhaul.config.GetValue(Settings.Stability_Regen_Speed)) * Time.deltaTime, 0f, 100f);
                            At.SetValue(num2, typeof(Character), self, "m_stability");
                        }
                        else if (m_shieldStability < 50f)
                        {
                            var num2 = Mathf.Clamp(m_shieldStability + self.StabilityRegen * Time.deltaTime, 0f, 50f);
                            At.SetValue(num2, typeof(Character), self, "m_shieldStability");
                        }
                        if (num > (float)CombatOverhaul.config.GetValue(Settings.Enemy_AutoKD_Reset_Time))
                        {
                            bool flag = m_knockbackCount > 0;
                            var  num2 = Mathf.Clamp(m_knockbackCount - Time.deltaTime, 0f, 4f);
                            if ((bool)CombatOverhaul.config.GetValue(Settings.BossPoise))
                            {
                                if ((int)EnemyClass.getEnemyLevel(self) < (int)EnemyLevel.MINIBOSS)
                                {
                                    At.SetValue(num2, typeof(Character), self, "m_knockbackCount");
                                }
                            }
                            else
                            {
                                At.SetValue(num2, typeof(Character), self, "m_knockbackCount");
                            }
                            if (flag && num2 <= 0)
                            {
                                // Debug.Log("Resetting AI stagger count for " + self.Name);
                            }
                        }
                    }

                    if ((bool)CombatOverhaul.config.GetValue(Settings.Poise))
                    {
                        if (num > (float)CombatOverhaul.config.GetValue(Settings.PoiseResetTime))
                        {
                            bool resetStability = false;

                            if ((bool)CombatOverhaul.config.GetValue(Settings.BossPoise))
                            {
                                //Minibosses do NOT regenerate stamina.
                                if ((int)EnemyClass.getEnemyLevel(self) < (int)EnemyLevel.MINIBOSS)
                                {
                                    resetStability = true;
                                }
                            }
                            else
                            {
                                resetStability = true;
                            }
                            //Initially for readability, allowing display of stability hurt on a stagger actually meant (even badly) coordinated strikes knocked down too much.
                            if (resetStability /*&& num > 0.25*/)
                            {
                                At.SetValue(100f, typeof(Character), self, "m_stability");
                            }
                        }
                    }
                }
                return(false);
            }