diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 74b761f9101..3161613a163 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -390,6 +390,19 @@ void CreatureAI::SetBoundary(CreatureBoundary const* boundary, bool negateBounda me->DoImmediateBoundaryCheck(); } +void CreatureAI::CheckDistanceToCurrentVictim() +{ + if (Unit* victim = me->GetVictim()) + { + Position victimPos; + victimPos.Relocate(victim->GetPosition()); + + // If we are closer than 50% of the combat reach we are going to reposition ourself + if (me->GetDistance(victimPos) < CalculatePct(me->GetCombatReach(), 50)) + me->MoveAdvanceTo(victim); + } +} + Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType) { return me->SummonCreature(entry, pos, summonType, despawnTime); diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index dc9483f762c..d2fd45bcc4c 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -206,6 +206,9 @@ class TC_GAME_API CreatureAI : public UnitAI static bool IsInBounds(CreatureBoundary const& boundary, Position const* who); + // Called every 3 Seconds to check if the creature needs to reposition itself + void CheckDistanceToCurrentVictim(); + protected: virtual void MoveInLineOfSight(Unit* /*who*/); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d2e340c25ab..2ab296a6376 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -182,7 +182,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), -m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), +m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_andvanceMovementTime(3000), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f) @@ -619,7 +619,8 @@ void Creature::Update(uint32 diff) LastCharmerGUID.Clear(); } - // periodic check to see if the creature has passed an evade boundary + // periodic check to see if the creature has passed an evade boundary... + // or if the creature needs to reposition itself if (IsAIEnabled && !IsInEvadeMode() && IsInCombat()) { if (diff >= m_boundaryCheckTime) @@ -628,6 +629,15 @@ void Creature::Update(uint32 diff) m_boundaryCheckTime = 2500; } else m_boundaryCheckTime -= diff; + + if (diff >= m_andvanceMovementTime) + { + AI()->CheckDistanceToCurrentVictim(); + m_andvanceMovementTime = 3000; + } + else + m_andvanceMovementTime -= diff; + } // if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now @@ -718,6 +728,27 @@ void Creature::Update(uint32 diff) } } +void Creature::MoveAdvanceTo(Unit* target) +{ + if (!target) // Just in case + return; + + // Do not reposition ourself when we are not allowed to move + if (HasUnitState(UNIT_STATE_CASTING) || isMoving() || !CanFreeMove()) + return; + + float x, y, z; + GetNearPoint(target, x, y, z, 0.0f, 0.0f, target->GetAngle(this)); + Movement::MoveSplineInit init(this); + init.MoveTo(x, y, z, true, false); + + // Beasts move backwards instead of turning arround + if (GetCreatureTemplate()->type == CREATURE_TYPE_BEAST) + init.SetOrientationFixed(true); + + init.Launch(); +} + void Creature::RegenerateMana() { uint32 curValue = GetPower(POWER_MANA); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 32cf1c234df..7302e50837b 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -734,6 +734,9 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool CanGiveExperience() const; + // Handles the repositioning to the current target + void MoveAdvanceTo(Unit* target); + protected: bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0); bool InitEntry(uint32 entry, CreatureData const* data = nullptr); @@ -757,6 +760,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma uint32 m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check uint32 m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse uint32 m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons) + uint32 m_andvanceMovementTime; // (msecs) remaining time for next reposition update to avoid creatures standing inside each other ReactStates m_reactState; // for AI, not charmInfo void RegenerateMana();