diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index dce728c142..31f387ff0b 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -185,7 +185,7 @@ void CreatureAI::MoveInLineOfSight(Unit* who) !me->IsWithinDist(who, ATTACK_DISTANCE, true, false, false)) // if in combat and in dist - neutral to all can actually assist other creatures return; - if (me->HasReactState(REACT_AGGRESSIVE) && me->CanStartAttack(who)) + if (me->HasReactState(REACT_AGGRESSIVE) && me->CanStartAttack(who) && (me->IsAggroGracePeriodExpired() || me->GetMap()->Instanceable())) AttackStart(who); } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index f092428332..6eb3a5ea99 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -274,7 +274,7 @@ Creature::Creature(): Unit(), MovableMapObject(), m_groupLootTimer(0), lootingGr m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_alreadyCallForHelp(false), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false), - m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f),_sparringPct(0.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0), + m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f),_sparringPct(0.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), _aggroGracePeriodExpired(false), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0), _isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true) { m_regenTimer = CREATURE_REGEN_INTERVAL; @@ -944,6 +944,21 @@ void Creature::Update(uint32 diff) } } +void Creature::Heartbeat() +{ + Unit::Heartbeat(); + + // creatures should only attack surroundings initially after heartbeat has passed or until attacked + if (!_aggroGracePeriodExpired) + { + _aggroGracePeriodExpired = true; + + // trigger MoveInLineOfSight + Acore::CreatureAggroGracePeriodExpiredNotifier notifier(*this); + Cell::VisitObjects(this, notifier, GetVisibilityRange()); + } +} + bool Creature::IsFreeToMove() { uint32 moveFlags = m_movementInfo.GetMovementFlags(); @@ -2654,12 +2669,6 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const if (victim->IsCreature() && victim->ToCreature()->IsInEvadeMode()) return false; - // cannot attack if is during 5 second grace period, unless being attacked - if (m_respawnedTime && (GameTime::GetGameTime().count() - m_respawnedTime) < 5 && !IsEngagedBy(victim)) - { - return false; - } - // if victim is in FD and we can't see that if (victim->HasUnitFlag2(UNIT_FLAG2_FEIGN_DEATH) && !CanIgnoreFeignDeath()) { diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index b5d93cd3ba..3d86ff5624 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -69,6 +69,8 @@ public: [[nodiscard]] ObjectGuid::LowType GetSpawnId() const { return m_spawnId; } void Update(uint32 time) override; // overwrited Unit::Update + void Heartbeat() override; + void GetRespawnPosition(float& x, float& y, float& z, float* ori = nullptr, float* dist = nullptr) const; void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } @@ -187,6 +189,8 @@ public: void UpdateAttackPowerAndDamage(bool ranged = false) override; void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override; + bool IsAggroGracePeriodExpired() { return _aggroGracePeriodExpired; } + void LoadSparringPct(); [[nodiscard]] float GetSparringPct() const { return _sparringPct; } @@ -516,6 +520,8 @@ private: CreatureGroup* m_formation; bool TriggerJustRespawned; + bool _aggroGracePeriodExpired; + // Shared timer between mobs who assist another. // Damaging one extends leash range on all of them. mutable std::shared_ptr m_lastLeashExtensionTime; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 890c6de2b0..9d443c02f5 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -186,6 +186,25 @@ void AIRelocationNotifier::Visit(CreatureMapType& m) } } +void CreatureAggroGracePeriodExpiredNotifier::Visit(CreatureMapType& m) +{ + for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter) + { + Creature* c = iter->GetSource(); + CreatureUnitRelocationWorker(c, &i_creature); + CreatureUnitRelocationWorker(&i_creature, c); + } +} + +void CreatureAggroGracePeriodExpiredNotifier::Visit(PlayerMapType& m) +{ + for (PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) + { + Player* player = iter->GetSource(); + CreatureUnitRelocationWorker(&i_creature, player); + } +} + // Uses visibility map void MessageDistDeliverer::Visit(VisiblePlayersMap const& m) { diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index d08645acdc..78a93bf257 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -96,6 +96,14 @@ namespace Acore void Visit(CreatureMapType&); }; + struct CreatureAggroGracePeriodExpiredNotifier + { + Creature& i_creature; + CreatureAggroGracePeriodExpiredNotifier(Creature& c) : i_creature(c) {} + template void Visit(GridRefMgr&) {} + void Visit(CreatureMapType&); + void Visit(PlayerMapType&); + }; struct MessageDistDeliverer { WorldObject const* i_source;