From 9d1589afd4f018a8f584b66180a9ca6b04256fa0 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sat, 24 Feb 2024 15:18:07 -0500 Subject: [PATCH] Core/Creatures: Implemented CREATURE_STATIC_FLAG_4_NO_MELEE_APPROACH and fixed CREATURE_STATIC_FLAG_NO_MELEE_FLEE implementation (should cause fleeing from melee range) Port From (https://github.com/TrinityCore/TrinityCore/commit/a748fddfa47216e1f516303e3bca9b5a86823748) --- Source/Framework/Constants/CreatureConst.cs | 4 +- Source/Game/AI/CoreAI/CreatureAI.cs | 19 +++++++- Source/Game/AI/CoreAI/PassiveAI.cs | 33 ++++++++------ Source/Game/AI/ScriptedAI/ScriptedAI.cs | 2 +- Source/Game/AI/SmartScripts/SmartAI.cs | 4 +- Source/Game/AI/SmartScripts/SmartScript.cs | 2 +- Source/Game/Entities/Creature/Creature.cs | 45 +++++++++++++++++-- Source/Game/Entities/Unit/Unit.Movement.cs | 7 ++- .../Generators/FleeingMovementGenerator.cs | 10 ++--- Source/Game/Movement/MotionMaster.cs | 1 - 10 files changed, 93 insertions(+), 34 deletions(-) diff --git a/Source/Framework/Constants/CreatureConst.cs b/Source/Framework/Constants/CreatureConst.cs index 7c1dcc6f4..f599d2056 100644 --- a/Source/Framework/Constants/CreatureConst.cs +++ b/Source/Framework/Constants/CreatureConst.cs @@ -290,7 +290,7 @@ namespace Framework.Constants CombatPing = 0x00020000, Aquatic = 0x00040000, // Aka Water Only, CreatureTemplateMovement.Ground = 0 Amphibious = 0x00080000, // CreatureTemplateMovement.Swim = 1 - NoMelee = 0x00100000, // Prevents Melee(Does Not Prevent Chasing, Does Not Make Creature Passive). Not Sure What 'Flee' Means But Another Flag Is Named NoMeleeApproach + NoMeleeFlee = 0x00100000, // "No Melee (Flee)" Prevents melee (moves as-if feared, does not make creature passive) VisibleToGhosts = 0x00200000, // CreatureTypeFlagVisibleToGhosts PvpEnabling = 0x00400000, // Old UnitFlagPvpEnabling, Now UnitBytes2OffsetPvpFlag From UnitFieldBytes2 DoNotPlayWoundAnim = 0x00800000, // CreatureTypeFlagDoNotPlayWoundAnim @@ -390,7 +390,7 @@ namespace Framework.Constants DealsTripleDamageToPcControlledPets = 0x00000040, NoNpcDamageBelow85ptc = 0x00000080, ObeysTauntDiminishingReturns = 0x00000100, // CreatureFlagExtraObeysTauntDiminishingReturns - NoMeleeApproach = 0x00000200, + NoMeleeApproach = 0x00000200, // "No Melee (Approach)" Prevents melee (chases into melee range, does not make creature passive) UpdateCreatureRecordWhenInstanceChangesDifficulty = 0x00000400, // Used Only By Snobold Vassal CannotDaze = 0x00000800, // "Cannot Daze (Combat Stun)" FlatHonorAward = 0x00001000, diff --git a/Source/Game/AI/CoreAI/CreatureAI.cs b/Source/Game/AI/CoreAI/CreatureAI.cs index 65c457f40..6707b788e 100644 --- a/Source/Game/AI/CoreAI/CreatureAI.cs +++ b/Source/Game/AI/CoreAI/CreatureAI.cs @@ -27,9 +27,9 @@ namespace Game.AI protected TaskScheduler _scheduler = new(); protected InstanceScript _instanceScript; - public CreatureAI(Creature _creature) : base(_creature) + public CreatureAI(Creature creature) : base(creature) { - me = _creature; + me = creature; _moveInLOSLocked = false; } @@ -514,6 +514,21 @@ namespace Game.AI // called when the corpse of this creature gets removed public virtual void CorpseRemoved(long respawnDelay) { } + public override void AttackStart(Unit victim) + { + if (victim != null && me.Attack(victim, true)) + { + // Clear distracted state on attacking + if (me.HasUnitState(UnitState.Distracted)) + { + me.ClearUnitState(UnitState.Distracted); + me.GetMotionMaster().Clear(); + } + + me.StartDefaultCombatMovement(victim); + } + } + /// == Gossip system ================================ // Called when the dialog status between a player and the creature is requested. diff --git a/Source/Game/AI/CoreAI/PassiveAI.cs b/Source/Game/AI/CoreAI/PassiveAI.cs index 2aa09253f..98b728ee7 100644 --- a/Source/Game/AI/CoreAI/PassiveAI.cs +++ b/Source/Game/AI/CoreAI/PassiveAI.cs @@ -4,6 +4,7 @@ using Framework.Constants; using Game.Entities; using Game.Spells; +using System; namespace Game.AI { @@ -92,28 +93,34 @@ namespace Game.AI public class CritterAI : PassiveAI { - public CritterAI(Creature c) : base(c) + TimeTracker _evadeTimer; + + public CritterAI(Creature creature) : base(creature) { - me.SetReactState(ReactStates.Passive); + _evadeTimer = new(); + me.SetCanMelee(false, true); } public override void JustEngagedWith(Unit who) { - if (!me.HasUnitState(UnitState.Fleeing)) - me.SetControlled(true, UnitState.Fleeing); + me.StartDefaultCombatMovement(who); + _evadeTimer.Reset(TimeSpan.FromMilliseconds(WorldConfig.GetIntValue(WorldCfg.CreatureFamilyFleeDelay))); } - public override void MovementInform(MovementGeneratorType type, uint id) + public override void UpdateAI(uint diff) { - if (type == MovementGeneratorType.TimedFleeing) - EnterEvadeMode(EvadeReason.Other); - } + if (me.IsEngaged()) + { + if (!me.IsInCombat()) + { + EnterEvadeMode(EvadeReason.NoHostiles); + return; + } - public override void EnterEvadeMode(EvadeReason why) - { - if (me.HasUnitState(UnitState.Fleeing)) - me.SetControlled(false, UnitState.Fleeing); - base.EnterEvadeMode(why); + _evadeTimer.Update(diff); + if (_evadeTimer.Passed()) + EnterEvadeMode(EvadeReason.Other); + } } } diff --git a/Source/Game/AI/ScriptedAI/ScriptedAI.cs b/Source/Game/AI/ScriptedAI/ScriptedAI.cs index 5e8f39deb..1a6a05e7f 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedAI.cs @@ -52,7 +52,7 @@ namespace Game.AI public void DoStartMovement(Unit target, float distance = 0.0f, float angle = 0.0f) { if (target != null) - me.GetMotionMaster().MoveChase(target, distance, angle); + me.StartDefaultCombatMovement(target, distance, angle); } //Start no movement on victim diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index 7c68be735..738257a7a 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -618,7 +618,7 @@ namespace Game.AI if (_canCombatMove) { SetRun(_run); - me.GetMotionMaster().MoveChase(who); + me.StartDefaultCombatMovement(who); } } } @@ -830,7 +830,7 @@ namespace Game.AI })) { SetRun(_run); - me.GetMotionMaster().MoveChase(me.GetVictim()); + me.StartDefaultCombatMovement(me.GetVictim()); } } else diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 8a37d44ee..f90bd0c45 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -1515,7 +1515,7 @@ namespace Game.AI if (creature != null) if (IsSmart(creature) && creature.GetVictim() != null) if (((SmartAI)creature.GetAI()).CanCombatMove()) - creature.GetMotionMaster().MoveChase(creature.GetVictim(), attackDistance, attackAngle); + creature.StartDefaultCombatMovement(creature.GetVictim(), attackDistance, attackAngle); } break; } diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 4d1f4f168..6812c9066 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -2602,6 +2602,47 @@ namespace Game.Entities Global.WorldMgr.SendGlobalMessage(packet, null, (enemy_team == Team.Alliance ? Team.Horde : Team.Alliance)); } + public void SetCanMelee(bool canMelee, bool fleeFromMelee = false) + { + bool wasFleeingFromMelee = HasFlag(CreatureStaticFlags.NoMeleeFlee); + + _staticFlags.ApplyFlag(CreatureStaticFlags.NoMeleeFlee, !canMelee && fleeFromMelee); + _staticFlags.ApplyFlag(CreatureStaticFlags4.NoMeleeApproach, !canMelee && !fleeFromMelee); + + if (wasFleeingFromMelee == HasFlag(CreatureStaticFlags.NoMeleeFlee)) + return; + + Unit victim = GetVictim(); + if (victim == null) + return; + + var currentMovement = GetMotionMaster().GetCurrentMovementGenerator(); + if (currentMovement == null) + return; + + var canChangeMovement = new Func(() => + { + if (wasFleeingFromMelee) + return currentMovement.GetMovementGeneratorType() == MovementGeneratorType.Fleeing && !HasUnitFlag(UnitFlags.Fleeing); + + return currentMovement.GetMovementGeneratorType() == MovementGeneratorType.Chase; + })(); + + if (!canChangeMovement) + return; + + GetMotionMaster().Remove(currentMovement); + StartDefaultCombatMovement(victim); + } + + public void StartDefaultCombatMovement(Unit victim, float? range = null, float? angle = null) + { + if (!HasFlag(CreatureStaticFlags.NoMeleeFlee) || IsSummon()) + GetMotionMaster().MoveChase(victim, range.GetValueOrDefault(0), angle.GetValueOrDefault(0)); + else + GetMotionMaster().MoveFleeing(victim); + } + public override bool HasSpell(uint spellId) { return m_spells.Contains(spellId); @@ -3376,9 +3417,7 @@ namespace Game.Entities public override SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = WeaponAttackType.BaseAttack) { return m_meleeDamageSchoolMask; } public void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = (SpellSchoolMask)(1 << (int)school); } - public bool CanMelee() { return !_staticFlags.HasFlag(CreatureStaticFlags.NoMelee); } - - public void SetCanMelee(bool canMelee) { _staticFlags.ApplyFlag(CreatureStaticFlags.NoMelee, !canMelee); } + public bool CanMelee() { return !_staticFlags.HasFlag(CreatureStaticFlags.NoMeleeFlee) && !_staticFlags.HasFlag(CreatureStaticFlags4.NoMeleeApproach); } public bool CanIgnoreLineOfSightWhenCastingOnMe() { return _staticFlags.HasFlag(CreatureStaticFlags4.IgnoreLosWhenCastingOnMe); } diff --git a/Source/Game/Entities/Unit/Unit.Movement.cs b/Source/Game/Entities/Unit/Unit.Movement.cs index 5f9213c86..c4b4d3d0c 100644 --- a/Source/Game/Entities/Unit/Unit.Movement.cs +++ b/Source/Game/Entities/Unit/Unit.Movement.cs @@ -1391,14 +1391,17 @@ namespace Game.Entities if (caster == null) caster = GetAttackerForHelper(); GetMotionMaster().MoveFleeing(caster, TimeSpan.FromMilliseconds(fearAuras.Empty() ? WorldConfig.GetIntValue(WorldCfg.CreatureFamilyFleeDelay) : 0)); // caster == NULL processed in MoveFleeing + SetUnitFlag(UnitFlags.Fleeing); } else { + RemoveUnitFlag(UnitFlags.Fleeing); if (IsAlive()) { GetMotionMaster().Remove(MovementGeneratorType.Fleeing); - if (GetVictim() != null) - SetTarget(GetVictim().GetGUID()); + Unit victim = GetVictim(); + if (victim != null) + SetTarget(victim.GetGUID()); if (!IsPlayer() && !IsInCombat()) GetMotionMaster().MoveTargetedHome(); } diff --git a/Source/Game/Movement/Generators/FleeingMovementGenerator.cs b/Source/Game/Movement/Generators/FleeingMovementGenerator.cs index 67fbcc2a8..cd86526ab 100644 --- a/Source/Game/Movement/Generators/FleeingMovementGenerator.cs +++ b/Source/Game/Movement/Generators/FleeingMovementGenerator.cs @@ -32,8 +32,6 @@ namespace Game.Movement if (owner == null || !owner.IsAlive()) return; - // TODO: UNIT_FIELD_FLAGS should not be handled by generators - owner.SetUnitFlag(UnitFlags.Fleeing); _path = null; SetTargetLocation(owner); } @@ -83,16 +81,15 @@ namespace Game.Movement { if (owner.IsPlayer()) { - owner.RemoveUnitFlag(UnitFlags.Fleeing); owner.ClearUnitState(UnitState.FleeingMove); owner.StopMoving(); } else { - owner.RemoveUnitFlag(UnitFlags.Fleeing); owner.ClearUnitState(UnitState.FleeingMove); - if (owner.GetVictim() != null) - owner.SetTarget(owner.GetVictim().GetGUID()); + Unit victim = owner.GetVictim(); + if (victim != null) + owner.SetTarget(victim.GetGUID()); } } } @@ -220,7 +217,6 @@ namespace Game.Movement if (!active) return; - owner.RemoveUnitFlag(UnitFlags.Fleeing); Unit victim = owner.GetVictim(); if (victim != null) { diff --git a/Source/Game/Movement/MotionMaster.cs b/Source/Game/Movement/MotionMaster.cs index 5e90127ca..c2f0666b5 100644 --- a/Source/Game/Movement/MotionMaster.cs +++ b/Source/Game/Movement/MotionMaster.cs @@ -563,7 +563,6 @@ namespace Game.Movement } public void MoveChase(Unit target, float dist, float angle = 0.0f) { MoveChase(target, new ChaseRange(dist), new ChaseAngle(angle)); } - public void MoveChase(Unit target, float dist) { MoveChase(target, new ChaseRange(dist)); } public void MoveChase(Unit target, ChaseRange? dist = null, ChaseAngle? angle = null) { // Ignore movement request if target not exist