private void SendDodge(Character self, float staminaCost, Vector3 _direction) { float f = (float)At.GetField(self.Stats, "m_stamina"); if (f >= staminaCost) { //At.SetValue(f - staminaCost, typeof(CharacterStats), self.Stats, "m_stamina"); self.Stats.UseStamina(TagSourceManager.Dodge, staminaCost); At.SetField(self, "m_dodgeAllowedInAction", 0); if (self.CharacterCamera && self.CharacterCamera.InZoomMode) { self.SetZoomMode(false); } self.ForceCancel(false, true); self.ResetCastType(); self.photonView.RPC("SendDodgeTriggerTrivial", PhotonTargets.All, new object[] { _direction }); At.Invoke(self, "ActionPerformed", false); self.Invoke("ResetDodgeTrigger", 0.5f); } }
public static bool Prefix(CharacterStats __instance) { var self = __instance; if (At.GetField(self, "m_timeOfLastStamUse") is float timeOfLast && Time.time - timeOfLast > (float)CombatOverhaul.config.GetValue(Settings.Stamina_Regen_Delay) && At.GetField(self, "m_stamina") is float m_stamina && At.GetField(self, "m_character") is Character character && !character.Blocking) { float regen = (float)CombatOverhaul.config.GetValue(Settings.Extra_Stamina_Regen) * Time.deltaTime; float newStamina = Mathf.Clamp(m_stamina + regen, 0, self.ActiveMaxStamina); At.SetField(self, "m_stamina", newStamina); } return(true); }
public static bool Prefix(Character __instance, Vector3 _direction) { var self = __instance; if (!(bool)CombatOverhaul.config.GetValue(Settings.Custom_Bag_Burden)) { return(true); } if (self.CurrentWeapon) { if (self.HasDodgeDirection) { self.Animator.SetFloat("DodgeBlend", !self.DodgeRestricted ? 0.0f : Instance.GetDodgeRestriction(self)); } } self.Animator.SetTrigger("Dodge"); if (self.CurrentlyChargingAttack) { //self.SendCancelCharging(); At.Invoke(self, "SendCancelCharging"); } // get sound player with null coalescing operator (At.GetField(self, "m_dodgeSoundPlayer") as SoundPlayer)?.Play(false); //self.m_dodging = true; At.SetField(self, "m_dodging", true); //self.StopBlocking(); At.Invoke(self, "StopBlocking"); // null coalescing OnDodgeEvent invoke self.OnDodgeEvent?.Invoke(); if (At.GetField(self, "m_characterSoundManager") is CharacterSoundManager charSounds) { Global.AudioManager.PlaySoundAtPosition(charSounds.GetDodgeSound(), self.transform, 0f, 1f, 1f, 1f, 1f); } self.SendMessage("DodgeTrigger", _direction, SendMessageOptions.DontRequireReceiver); return(false); }
public static bool Prefix(Character __instance) { var self = __instance; if (At.GetField(self, "m_stability") is float m_stability && At.GetField(self, "m_timeOfLastStabilityHit") is float m_timeOfLastStabilityHit && At.GetField(self, "m_shieldStability") is float m_shieldStability && At.GetField(self, "m_knockbackCount") is float m_knockbackCount) { // ----------- 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) { var num2 = Mathf.Clamp(m_stability + (self.StabilityRegen * (float)CombatOverhaul.config.GetValue(Settings.Stability_Regen_Speed)) * Time.deltaTime, 0f, 100f); At.SetField(self, "m_stability", num2); } else if (m_shieldStability < 50f) { var num2 = Mathf.Clamp(m_shieldStability + self.StabilityRegen * Time.deltaTime, 0f, 50f); At.SetField(self, "m_shieldStability", num2); } 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); At.SetField(self, "m_knockbackCount", num2); if (flag && num2 <= 0) { // Debug.Log("Resetting AI stagger count for " + self.Name); } } } } return(false); }
public static bool Prefix(Character __instance, bool _down, Vector3 _dir) { var self = __instance; var _base = self as Photon.MonoBehaviour; if (At.GetField(self, "m_stability") is float m_stability) { float staggerVal = Mathf.Clamp(m_stability - (float)CombatOverhaul.config.GetValue(Settings.Stagger_Threshold), 1f, 100 - (float)CombatOverhaul.config.GetValue(Settings.Stagger_Threshold)); At.Invoke(self, "StabilityHit", new object[] { (!_down) ? staggerVal : m_stability, Vector3.Angle(_base.transform.forward, -_dir), _down, null }); } return(false); }
public static bool Prefix(Character __instance, Vector3 _direction) { var self = __instance; // only use this hook for local players. return orig everything else if (self.IsAI || !self.IsPhotonPlayerLocal) { return(true); } float staminaCost = (float)CombatOverhaul.config.GetValue(Settings.Custom_Dodge_Cost); // if dodge cancelling is NOT enabled, just do a normal dodge check. if (!(bool)CombatOverhaul.config.GetValue(Settings.Dodge_Cancelling)) { if (At.GetField(self, "m_currentlyChargingAttack") is bool m_currentlyChargingAttack && At.GetField(self, "m_preparingToSleep") is bool m_preparingToSleep && At.GetField(self, "m_nextIsLocomotion") is bool m_nextIsLocomotion && At.GetField(self, "m_dodgeAllowedInAction") is int m_dodgeAllowedInAction) { if (self.Stats.MovementSpeed > 0f && !m_preparingToSleep && (!self.LocomotionAction || m_currentlyChargingAttack) && (m_nextIsLocomotion || m_dodgeAllowedInAction > 0)) { if (!self.Dodging) { Instance.SendDodge(self, staminaCost, _direction); } return(false); } } } else // cancelling enabled. check if we should allow the dodge { if (Instance.PlayerLastHitTimes.ContainsKey(self.UID) && Time.time - Instance.PlayerLastHitTimes[self.UID] < (float)CombatOverhaul.config.GetValue(Settings.Dodge_DelayAfterHit)) { // Debug.Log("Player has hit within the last few seconds. Dodge not allowed!"); return(false); } Character.HurtType hurtType = (Character.HurtType)At.GetField(self, "m_hurtType"); // manual fix (game sometimes does not reset HurtType to NONE when animation ends. float timeout = (float)CombatOverhaul.config.GetValue(Settings.Dodge_DelayAfterStagger); if (hurtType == Character.HurtType.Knockdown) { timeout = (float)CombatOverhaul.config.GetValue(Settings.Dodge_DelayAfterKD); } if ((float)At.GetField(self, "m_timeOfLastStabilityHit") is float lasthit && Time.time - lasthit > timeout) { hurtType = Character.HurtType.NONE; At.SetField(self, "m_hurtType", hurtType); } // if we're not currently dodging or staggered, force an animation cancel dodge (provided we have enough stamina). if (!self.Dodging && hurtType == Character.HurtType.NONE) { Instance.SendDodge(self, staminaCost, _direction); } // send a fix to force m_dodging to false after a short delay. // this is a fix for if the player dodges while airborne, the game wont reset their m_dodging to true when they land. Instance.StartCoroutine(Instance.DodgeLateFix(self)); } return(false); }
public static void Postfix(Character __instance, Vector3 _direction, bool ___m_pendingDeath, ref int ___m_dodgeAllowedInAction) { if (!CombatTweaksMod.Dodge_Cancelling.Value) { return; } if (!__instance.IsPhotonPlayerLocal || __instance.IsAI || __instance.Dodging) { return; } if (___m_pendingDeath) { return; } // check player has enough stamina if (!(bool)At.Invoke(__instance, "HasEnoughStamina", (float)__instance.DodgeStamCost)) { return; } if (PlayerLastHitTimes.ContainsKey(__instance.UID) && Time.time - PlayerLastHitTimes[__instance.UID] < CombatTweaksMod.Dodge_DelayAfterPlayerHits.Value) { // Debug.Log("Player has hit within the last few seconds. Dodge not allowed!"); return; } Character.HurtType hurtType = (Character.HurtType)At.GetField(__instance, "m_hurtType"); // manual fix (game sometimes does not reset HurtType to NONE when animation ends. float timeout; if (hurtType == Character.HurtType.Knockdown) { timeout = CombatTweaksMod.Dodge_DelayAfterKnockdown.Value; } else { timeout = CombatTweaksMod.Dodge_DelayAfterStagger.Value; } if ((float)At.GetField(__instance, "m_timeOfLastStabilityHit") is float lasthit && Time.time - lasthit > timeout) { hurtType = Character.HurtType.NONE; At.SetField(__instance, "m_hurtType", hurtType); } // if we're not currently staggered, force an animation cancel dodge (provided we have enough stamina). if (hurtType == Character.HurtType.NONE) { //SendDodge(__instance, __instance.DodgeStamCost, _direction); __instance.Stats.UseStamina(TagSourceManager.Dodge, __instance.DodgeStamCost); ___m_dodgeAllowedInAction = 0; if (__instance.CharacterCamera && __instance.CharacterCamera.InZoomMode) { __instance.SetZoomMode(false); } __instance.ForceCancel(false, true); __instance.ResetCastType(); __instance.photonView.RPC("SendDodgeTriggerTrivial", PhotonTargets.All, new object[] { _direction }); At.Invoke(__instance, "ActionPerformed", true); __instance.Invoke("ResetDodgeTrigger", 0.5f); } // send a fix to force m_dodging to false after a short delay. // this is a fix for if the player dodges while airborne, the game wont reset their m_dodging to true when they land. CombatTweaksMod.Instance.StartCoroutine(DodgeLateFix(__instance)); }
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.GetField(self, "m_impactImmune") is bool m_impactImmune && At.GetField(self, "m_shieldStability") is float m_shieldStability && At.GetField(self, "m_stability") is float m_stability && At.GetField(self, "m_knockbackCount") is float m_knockbackCount && At.GetField(self, "m_knockHurtAllowed") is bool m_knockHurtAllowed && At.GetField(self, "m_currentlyChargingAttack") is bool m_currentlyChargingAttack && At.GetField(self, "m_animator") is Animator m_animator) { // 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.SetField(self, "m_timeOfLastStabilityHit", Time.time); // 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.SetField(self, "m_stability", num2); m_stability = num2; } var num3 = Mathf.Clamp(m_shieldStability - hit, 0f, 50f); At.SetField(self, "m_shieldStability", num3); m_shieldStability = num3; } // check non-blocking stability (unchanged) else { var num2 = Mathf.Clamp(m_stability - hit, 0f, 100f); At.SetField(self, "m_stability", num2); m_stability = num2; } // if hit takes us below knockdown threshold, or if AI auto-knockdown stagger count was reached... if (m_stability <= (float)CombatOverhaul.config.GetValue(Settings.Knockdown_Threshold) || (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.Invoke(self, "Knock", true); } At.SetField(self, "m_stability", 0f); m_stability = 0f; if (self.IsPhotonPlayerLocal) { self.BlockInput(false); } } // else if hit is a stagger... else if (m_stability <= (float)CombatOverhaul.config.GetValue(Settings.Stagger_Threshold) && (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))) { _base.photonView.RPC("SendKnock", PhotonTargets.All, new object[] { false, m_stability }); } else { At.Invoke(self, "Knock", true); } if (self.IsPhotonPlayerLocal && _block) { self.BlockInput(false); } } // else if we are not blocking... else if (!_block) { // Debug.Log("Value: " + _knockValue + ", new stability: " + m_stability); if (m_knockHurtAllowed) { At.SetField(self, "m_hurtType", Character.HurtType.Hurt); if (m_currentlyChargingAttack) { self.CancelCharging(); } m_animator.SetTrigger("Knockhurt"); _base.StopCoroutine("KnockhurtRoutine"); MethodInfo _knockhurtRoutine = self.GetType().GetMethod("KnockhurtRoutine", BindingFlags.NonPublic | 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); }
// actual function to set an enemy's stats private void SetEnemyMods(ModConfig _config, CharacterStats _stats, Character m_character) { if (m_character == null || !m_character.IsAI || m_character.Faction == Character.Factions.Player) { //Debug.Log("trying to set stats for a null character, or a non-AI character"); return; } if (FixedEnemies.Contains(m_character.UID)) { // Debug.Log("Fixed enemies already contains " + m_character.Name); return; } var m_staminaUseModiifers = new Stat(1f); m_staminaUseModiifers.AddMultiplierStack(new StatStack("MyStat", -0.9f)); if ((bool)_config.GetValue(Settings.Enemy_Balancing)) { string stackSource = "CombatOverhaul"; if (!PhotonNetwork.isNonMasterClientInRoom) { // set health modifier var healthTag = TagSourceManager.Instance.GetTag("77"); // 77 = max health var healthStack = new StatStack(stackSource, (float)_config.GetValue(Settings.Enemy_Health) - 1); _stats.RemoveStatStack(healthTag, stackSource, true); _stats.AddStatStack(healthTag, healthStack, true); At.SetField(_stats, "m_health", _stats.CurrentHealth * (float)_config.GetValue(Settings.Enemy_Health)); } // set impact resistance var impactTag = TagSourceManager.Instance.GetTag("84"); // 84 = impact res _stats.RemoveStatStack(impactTag, stackSource, false); var impactStack = new StatStack(stackSource, (float)_config.GetValue(Settings.Enemy_ImpactRes)); _stats.AddStatStack(impactTag, impactStack, false); // damage bonus var damageTag = TagSourceManager.Instance.GetTag("96"); // 96 = all damage bonus _stats.RemoveStatStack(damageTag, stackSource, true); var damageStack = new StatStack(stackSource, (float)_config.GetValue(Settings.Enemy_Damages) * 0.01f); _stats.AddStatStack(damageTag, damageStack, true); // impact modifier var impactModifier = At.GetField(_stats, "m_impactModifier") as Stat; impactModifier.RemoveStack(stackSource, true); impactModifier.AddStack(new StatStack(stackSource, (float)_config.GetValue(Settings.Enemy_ImpactDmg) * 0.01f), true); for (int i = 0; i < 6; i++) { // damage resistance (Capped at 99, unless already 100) float currentRes = m_character.Stats.GetDamageResistance((DamageType.Types)i); if (currentRes < 100) { var valueToSet = (float)_config.GetValue(Settings.Enemy_Resistances); if (currentRes + valueToSet >= 99) { valueToSet = 99 - currentRes; } int tag = 113 + i; // 113 to 118 = damage resistance stats var damageResTag = TagSourceManager.Instance.GetTag(tag.ToString()); _stats.RemoveStatStack(damageResTag, stackSource, true); var resStack = new StatStack(stackSource, valueToSet); _stats.AddStatStack(damageResTag, resStack, false); } } } if ((bool)_config.GetValue(Settings.All_Enemies_Allied)) { m_character.ChangeFaction(Character.Factions.Bandits); m_character.TargetingSystem.AlliedToSameFaction = true; Character.Factions[] targets = new Character.Factions[] { Character.Factions.Player }; At.SetField(m_character.TargetingSystem, "TargetableFactions", targets); // fix skills foreach (var uid in m_character.Inventory.SkillKnowledge.GetLearnedActiveSkillUIDs()) { if (ItemManager.Instance.GetItem(uid) is Skill skill) { foreach (Shooter shooter in skill.GetComponentsInChildren <Shooter>()) { shooter.Setup(targets, shooter.transform.parent); } } } } FixedEnemies.Add(m_character.UID); }