diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index 9f08ceb37..5240c88e5 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -253,6 +253,9 @@ SpellCastResult UnitAI::DoCastAOE(uint32 spellId, bool triggered) return me->CastSpell((Unit*)nullptr, spellId, triggered); } +/** + * @brief Cast the spell on a random unit from the threat list + */ SpellCastResult UnitAI::DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition, float dist, bool playerOnly, bool triggered, bool withTank) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, threatTablePosition, dist, playerOnly, withTank)) diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 2f9bba321..0f7c50163 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -198,14 +198,14 @@ public: virtual ~UnitAI() {} virtual bool CanAIAttack(Unit const* /*target*/) const { return true; } - virtual void AttackStart(Unit* /*target*/); + virtual void AttackStart(Unit* /*target*/); /// @brief Use to start attacking a target. Called just before JustEngagedWith() virtual void UpdateAI(uint32 /*diff*/) = 0; virtual void InitializeAI() { if (!me->isDead()) Reset(); } virtual void Reset() {}; - // Called when unit is charmed + /// @brief Called when unit is charmed virtual void OnCharmed(bool apply) = 0; // Pass parameters between AI @@ -258,16 +258,16 @@ public: } } - // Select the best (up to) targets (in order) from the threat list that fulfill the following: - // - Not among the first entries in order (or SelectTargetMethod::MaxThreat order, - // if is SelectTargetMethod::Random). - // - Within at most yards (if dist > 0.0f) - // - At least - yards away (if dist < 0.0f) - // - Is a player (if playerOnly = true) - // - Not the current tank (if withTank = false) - // - Has aura with ID (if aura > 0) - // - Does not have aura with ID - (if aura < 0) - // The resulting targets are stored in (which is cleared first). + /** @brief Select the best (up to) targets (in order) from the threat list that fulfill the following: + * - Not among the first entries in order (or SelectTargetMethod::MaxThreat order, if is SelectTargetMethod::Random). + * - Within at most yards (if dist > 0.0f) + * - At least - yards away (if dist < 0.0f) + * - Is a player (if playerOnly = true) + * - Not the current tank (if withTank = false) + * - Has aura with ID (if aura > 0) + * - Does not have aura with ID - (if aura < 0) + * The resulting targets are stored in (which is cleared first). + */ void SelectTargetList(std::list& targetList, uint32 num, SelectTargetMethod targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int32 aura = 0); // Select the best (up to) targets (in order) satisfying from the threat list and stores them in (which is cleared first). @@ -345,7 +345,7 @@ public: /** * @brief Called when the unit enters combat - * (NOTE: Creature engage logic should NOT be here, but in JustEngagedWith, which happens once threat is established!) + * @note NOTE: Creature engage logic should NOT be here, but in JustEngagedWith, which happens once threat is established!) * * @todo Never invoked right now. Preparation for Combat Threat refactor */ @@ -358,28 +358,30 @@ public: */ virtual void JustExitedCombat() { } - // Called at any Damage to any victim (before damage apply) + /// @brief Called at any Damage to any victim (before damage apply) virtual void DamageDealt(Unit* /*victim*/, uint32& /*damage*/, DamageEffectType /*damageType*/) { } - // Called at any Damage from any attacker (before damage apply) - // Note: it for recalculation damage or special reaction at damage - // for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also + /** @brief Called at any Damage from any attacker (before damage apply) + * + * @note It use for recalculation damage or special reaction at damage + * for attack reaction use AttackedBy called for non DOT damage in Unit::DealDamage also + */ virtual void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) {} - // Called when the creature receives heal + /// @brief Called when the creature receives heal virtual void HealReceived(Unit* /*done_by*/, uint32& /*addhealth*/) {} - // Called when the creature power updates + /// @brief Called when the creature power updates virtual void OnPowerUpdate(Powers /*power*/, int32 /*updateVal*/, int32 /*gain*/, uint32 /*currPower*/) {} - // Called when the unit heals + /// @brief Called when the unit heals virtual void HealDone(Unit* /*done_to*/, uint32& /*addhealth*/) {} - // Called during damage calculations + /// @brief Called during damage calculations virtual void OnCalculateMeleeDamageReceived(uint32& /*damage*/, Unit* /*attacker*/) {} virtual void OnCalculateSpellDamageReceived(int32& /*damage*/, Unit* /*attacker*/) {} - // Called during calculation when receiving periodic healing or damage (DoT or HoT) + /// @brief Called during calculation when receiving periodic healing or damage (DoT or HoT) virtual void OnCalculatePeriodicTickReceived(uint32& /*damage*/, Unit* /*attacker*/) {} void AttackStartCaster(Unit* victim, float dist); @@ -387,13 +389,13 @@ public: SpellCastResult DoAddAuraToAllHostilePlayers(uint32 spellid); SpellCastResult DoCast(uint32 spellId); SpellCastResult DoCast(Unit* victim, uint32 spellId, bool triggered = false); - SpellCastResult DoCastSelf(uint32 spellId, bool triggered = false) { return DoCast(me, spellId, triggered); } + SpellCastResult DoCastSelf(uint32 spellId, bool triggered = false) { return DoCast(me, spellId, triggered); } /// @brief To specify the caster as target if the spell is self-cast SpellCastResult DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false); SpellCastResult DoCastVictim(uint32 spellId, bool triggered = false); SpellCastResult DoCastAOE(uint32 spellId, bool triggered = false); SpellCastResult DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false, bool withTank = true); - // Cast spell on the top threat target, which may not be the current victim. + /// @brief Cast spell on the top threat target, which may not be the current victim. SpellCastResult DoCastMaxThreat(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false); float DoGetSpellMaxRange(uint32 spellId, bool positive = false); @@ -405,7 +407,7 @@ public: static AISpellInfoType* AISpellInfo; static void FillAISpellInfo(); - // Called when a summon reaches a waypoint or point movement finished. + /// @brief Called when a summon reaches a waypoint or point movement finished. virtual void SummonMovementInform(Creature* /*creature*/, uint32 /*motionType*/, uint32 /*point*/) { } virtual void sGossipHello(Player* /*player*/) {} diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index ff9f03c17..cae76f330 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1945,11 +1945,21 @@ bool Creature::CanStartAttack(Unit const* who) const return IsWithinLOSInMap(who); } -void Creature::setDeathState(DeathState s, bool despawn) +/** + * @brief A creature can be in 4 different states: Alive, JustDied, Corpse, and JustRespawned. The cycle follows the next order: + * - Alive: The creature is alive and has spawned in the world + * - JustDied: The creature has just died. This is the state just before his body appeared + * - Corpse: The creature has been removed from the world, and this corpse has been spawned + * - JustRespawned: The creature has just respawned. Follow when the corpse disappears and the respawn timer is finished + * + * @param state Specify one of 4 states above + * @param despawn Despawn the creature immediately, without waiting for any movement to finish + */ +void Creature::setDeathState(DeathState state, bool despawn) { - Unit::setDeathState(s, despawn); + Unit::setDeathState(state, despawn); - if (s == DeathState::JustDied) + if (state == DeathState::JustDied) { _lastDamagedTime.reset(); @@ -1960,10 +1970,10 @@ void Creature::setDeathState(DeathState s, bool despawn) if (GetMap()->IsDungeon() || isWorldBoss() || GetCreatureTemplate()->rank >= CREATURE_ELITE_ELITE) SaveRespawnTime(); - SetTarget(); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState) + SetTarget(); // remove target selection in any cases (can be set at aura remove in Unit::setDeathState) ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); - Dismount(); // if creature is mounted on a virtual mount, remove it at death + Dismount(); // if creature is mounted on a virtual mount, remove it at death setActive(false); @@ -1985,10 +1995,8 @@ void Creature::setDeathState(DeathState s, bool despawn) Unit::setDeathState(DeathState::Corpse, despawn); } - else if (s == DeathState::JustRespawned) + else if (state == DeathState::JustRespawned) { - //if (IsPet()) - // setActive(true); SetFullHealth(); SetLootRecipient(nullptr); ResetPlayerDamageReq(); @@ -2016,6 +2024,9 @@ void Creature::setDeathState(DeathState s, bool despawn) } } +/** + * @param force Force the respawn by killing the creature. + */ void Creature::Respawn(bool force) { if (force) @@ -2040,7 +2051,7 @@ void Creature::Respawn(bool force) } bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning - if (!allowed && !force) // Will be rechecked on next Update call + if (!allowed && !force) // Will be rechecked on next Update call return; ObjectGuid dbtableHighGuid = ObjectGuid::Create(m_creatureData ? m_creatureData->id1 : GetEntry(), m_spawnId); @@ -2082,8 +2093,7 @@ void Creature::Respawn(bool force) setDeathState(DeathState::JustRespawned); - // MDic - Acidmanifesto - // Do not override transform auras + // MDic - Acidmanifesto: Do not override transform auras if (GetAuraEffectsByType(SPELL_AURA_TRANSFORM).empty()) { CreatureModel display(GetNativeDisplayId(), GetNativeObjectScale(), 1.0f); @@ -2102,7 +2112,7 @@ void Creature::Respawn(bool force) { //reset the AI to be sure no dirty or uninitialized values will be used till next tick AI()->Reset(); - TriggerJustRespawned = true;//delay event to next tick so all creatures are created on the map before processing + TriggerJustRespawned = true; //delay event to next tick so all creatures are created on the map before processing } uint32 poolid = m_spawnId ? sPoolMgr->IsPartOfAPool(m_spawnId) : 0; @@ -2120,7 +2130,7 @@ void Creature::Respawn(bool force) UpdateObjectVisibility(false); } - else // the master is dead + else // the master is dead { ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid); if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day) @@ -2148,7 +2158,7 @@ void Creature::ForcedDespawn(uint32 timeMSToDespawn, Seconds forceRespawnTimer) if (IsAlive()) setDeathState(DeathState::JustDied, true); - // Xinef: set new respawn time, ignore corpse decay time... + // Xinef: Set new respawn time, ignore corpse decay time... RemoveCorpse(true); if (forceRespawnTimer > Seconds::zero()) @@ -2194,8 +2204,6 @@ void Creature::InitializeReactState() SetReactState(REACT_PASSIVE); else SetReactState(REACT_AGGRESSIVE); - /*else if (IsCivilian()) - SetReactState(REACT_DEFENSIVE);*/ } bool Creature::HasMechanicTemplateImmunity(uint32 mask) const @@ -2357,8 +2365,7 @@ SpellInfo const* Creature::reachWithSpellCure(Unit* victim) float range = spellInfo->GetMaxRange(true); float minrange = spellInfo->GetMinRange(true); float dist = GetDistance(victim); - //if (!isInFront(victim, range) && spellInfo->AttributesEx) - // continue; + if (dist > range || dist < minrange) continue; if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE && HasUnitFlag(UNIT_FLAG_SILENCED)) @@ -2370,7 +2377,9 @@ SpellInfo const* Creature::reachWithSpellCure(Unit* victim) return nullptr; } -// select nearest hostile unit within the given distance (regardless of threat list). +/** + * @brief Select nearest hostile unit within the given distance (regardless of threat list). + */ Unit* Creature::SelectNearestTarget(float dist, bool playerOnly /* = false */) const { if (dist == 0.0f) @@ -2386,7 +2395,9 @@ Unit* Creature::SelectNearestTarget(float dist, bool playerOnly /* = false */) c return target; } -// select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list. +/** + * @brief Select nearest hostile unit within the given attack distance (i.e. distance is ignored if > than ATTACK_DISTANCE), regardless of threat list. + */ Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const { if (dist < ATTACK_DISTANCE) @@ -2780,6 +2791,9 @@ void Creature::SendZoneUnderAttackMessage(Player* attacker) sWorld->SendGlobalMessage(&data, nullptr, (attacker->GetTeamId() == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE)); } +/** + * @brief Set in combat all units in the dungeon/raid. Affect only units with IsAIEnabled. + */ void Creature::SetInCombatWithZone() { if (IsAIEnabled) @@ -3146,6 +3160,9 @@ bool Creature::IsImmuneToKnockback() const return cinfo && (cinfo->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK); } +/** + * @brief Enable or disable the creature's walk mode by removing: MOVEMENTFLAG_WALKING. Infom also the client + */ bool Creature::SetWalk(bool enable) { if (!Unit::SetWalk(enable)) @@ -3157,6 +3174,9 @@ bool Creature::SetWalk(bool enable) return true; } +/** + * @brief Enable or disable the creature's fly mode by adding or removing: MOVEMENTFLAG_FLYING. Infom also the client + */ bool Creature::SetDisableGravity(bool disable, bool packetOnly /*= false*/, bool updateAnimationTier /*= true*/) { //! It's possible only a packet is sent but moveflags are not updated diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 877f8e2ec..52f51dcdb 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -40,7 +40,7 @@ class CreatureGroup; // max different by z coordinate for creature aggro reaction #define CREATURE_Z_ATTACK_RANGE 3 -#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY +#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY class Creature : public Unit, public GridObject, public MovableMapObject { @@ -67,7 +67,7 @@ public: [[nodiscard]] ObjectGuid::LowType GetSpawnId() const { return m_spawnId; } - void Update(uint32 time) override; // overwrited Unit::Update + void Update(uint32 time) override; // overwrited Unit::Update void GetRespawnPosition(float& x, float& y, float& z, float* ori = nullptr, float* dist = nullptr) const; void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } @@ -88,9 +88,15 @@ public: MovementGeneratorType GetDefaultMovementType() const override { return m_defaultMovementType; } void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; } - void SetReactState(ReactStates st) { m_reactState = st; } + /** + * @brief A creature can have 3 ReactStates : Agressive, Passive, Neutral + * - Agressive : The creature will attack any non friend units in sight + * - Passive : The creature will not attack anyone + * - Neutral : The creature will attack only if attacked + */ + void SetReactState(ReactStates state) { m_reactState = state; } [[nodiscard]] ReactStates GetReactState() const { return m_reactState; } - [[nodiscard]] bool HasReactState(ReactStates state) const { return (m_reactState == state); } + [[nodiscard]] bool HasReactState(ReactStates state) const { return (m_reactState == state); } /// @brief Check if the creature has the specified ReactState void InitializeReactState(); ///// @todo RENAME THIS!!!!! @@ -209,14 +215,14 @@ public: // override WorldObject function for proper name localization [[nodiscard]] std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override; - void setDeathState(DeathState s, bool despawn = false) override; // override virtual Unit::setDeathState + void setDeathState(DeathState s, bool despawn = false) override; // override virtual Unit::setDeathState bool LoadFromDB(ObjectGuid::LowType guid, Map* map, bool allowDuplicate = false) { return LoadCreatureFromDB(guid, map, false, allowDuplicate); } bool LoadCreatureFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true, bool allowDuplicate = false); void SaveToDB(); - // overriden in Pet - virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); - virtual void DeleteFromDB(); // overriden in Pet + + virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); // overriden in Pet + virtual void DeleteFromDB(); // overriden in Pet Loot loot; [[nodiscard]] ObjectGuid GetLootRecipientGUID() const { return m_lootRecipient; } @@ -224,7 +230,7 @@ public: [[nodiscard]] ObjectGuid::LowType GetLootRecipientGroupGUID() const { return m_lootRecipientGroup; } [[nodiscard]] Group* GetLootRecipientGroup() const; [[nodiscard]] bool hasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; } - bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party. + bool isTappedBy(Player const* player) const; // return true if the creature is tapped by the player or a member of his party. [[nodiscard]] bool CanGeneratePickPocketLoot() const; void SetPickPocketLootTime(); void ResetPickPocketLootTime() { lootPickPocketRestoreTime = 0; } @@ -266,7 +272,7 @@ public: bool _IsTargetAcceptable(Unit const* target) const; [[nodiscard]] bool CanIgnoreFeignDeath() const { return (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH) != 0; } - // pussywizard: updated at faction change, disable move in line of sight if actual faction is not hostile to anyone + // pussywizard: Updated at faction change, disable move in line of sight if actual faction is not hostile to anyone void UpdateMoveInLineOfSightState(); bool IsMoveInLineOfSightDisabled() { return m_moveInLineOfSightDisabled; } bool IsMoveInLineOfSightStrictlyDisabled() { return m_moveInLineOfSightStrictlyDisabled; } @@ -299,8 +305,8 @@ public: void DoImmediateBoundaryCheck() { m_boundaryCheckTime = 0; } - uint32 m_groupLootTimer; // (msecs)timer used for group loot - uint32 lootingGroupLowGUID; // used to find group which is looting corpse + uint32 m_groupLootTimer; // (msecs)timer used for group loot + uint32 lootingGroupLowGUID; // used to find group which is looting corpse void SendZoneUnderAttackMessage(Player* attacker); @@ -310,8 +316,8 @@ public: [[nodiscard]] bool hasInvolvedQuest(uint32 quest_id) const override; bool isRegeneratingHealth() { return m_regenHealth; } - void SetRegeneratingHealth(bool c) { m_regenHealth = c; } - void SetRegeneratingPower(bool c) { m_regenPower = c; } + void SetRegeneratingHealth(bool enable) { m_regenHealth = enable; } + void SetRegeneratingPower(bool enable) { m_regenPower = enable; } [[nodiscard]] virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; } [[nodiscard]] virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const { @@ -398,20 +404,17 @@ public: /** * @brief Helper to resume chasing current victim. - * - * */ + */ void ResumeChasingVictim() { GetMotionMaster()->MoveChase(GetVictim()); }; /** * @brief Returns true if the creature is able to cast the spell. - * - * */ + */ bool CanCastSpell(uint32 spellID) const; /** - * @brief Helper to get the creature's summoner GUID, if it is a summon - * - * */ + * @brief Helper to get the creature's summoner GUID, if it is a summon + */ [[nodiscard]] ObjectGuid GetSummonerGUID() const; // Used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims @@ -474,11 +477,11 @@ protected: bool DisableReputationGain; - CreatureTemplate const* m_creatureInfo; // in difficulty mode > 0 can different from sObjectMgr->GetCreatureTemplate(GetEntry()) + CreatureTemplate const* m_creatureInfo; // in difficulty mode > 0 can different from sObjectMgr->GetCreatureTemplate(GetEntry()) CreatureData const* m_creatureData; float m_detectionDistance; - uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable + uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable [[nodiscard]] bool IsInvisibleDueToDespawn() const override; bool CanAlwaysSee(WorldObject const* obj) const override; @@ -489,11 +492,11 @@ private: [[nodiscard]] bool CanPeriodicallyCallForAssistance() const; - //WaypointMovementGenerator vars + // WaypointMovementGenerator variable uint32 m_waypointID; uint32 m_path_id; - //Formation var + // Formation variable CreatureGroup* m_formation; bool TriggerJustRespawned; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f3aa3addf..4884f22e8 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1141,6 +1141,9 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage return damage; } + /** + * @brief Interrupt the unit cast for all the current spells + */ void Unit::CastStop(uint32 except_spellid, bool withInstant) { for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++) @@ -3034,6 +3037,10 @@ void Unit::SendMeleeAttackStart(Unit* victim, Player* sendTo) LOG_DEBUG("entities.unit", "WORLD: Sent SMSG_ATTACKSTART"); } +/** + * @brief Send to the client SMSG_ATTACKSTOP but doesn't clear UNIT_STATE_MELEE_ATTACKING on server side + * or interrupt spells. Unless you know exactly what you're doing, use AttackStop() or RemoveAllAttackers() instead + */ void Unit::SendMeleeAttackStop(Unit* victim) { // pussywizard: calling SendMeleeAttackStop without clearing UNIT_STATE_MELEE_ATTACKING and then AttackStart the same player may spoil npc rotating! @@ -10331,6 +10338,10 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) return true; } +/** + * @brief Force the unit to stop attacking. This will clear UNIT_STATE_MELEE_ATTACKING, + * Interrupt current spell, AI assistance, and call SendMeleeAttackStop() to the client + */ bool Unit::AttackStop() { if (!m_attacking) @@ -10407,6 +10418,9 @@ bool Unit::isAttackingPlayer() const return false; } +/** + * @brief Remove all units in m_attackers list and send them AttackStop() + */ void Unit::RemoveAllAttackers() { while (!m_attackers.empty()) @@ -16600,7 +16614,7 @@ void Unit::ResumeMovement(uint32 timer /* = 0*/, uint8 slot /* = 0*/) movementGenerator->Resume(timer); } -void Unit::StopMovingOnCurrentPos() // pussywizard +void Unit::StopMovingOnCurrentPos() { ClearUnitState(UNIT_STATE_MOVING); @@ -20487,6 +20501,12 @@ bool Unit::SetSwim(bool enable) return true; } +/** + * @brief Add the movement flag: MOVEMENTFLAGCAN_FLY. Generaly only use by players, allowing + * them to fly by pressing space for example. For creatures, please look for DisableGravity(). + * + * Doesn't inform the client. + */ bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */) { if (enable == HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY)) @@ -20505,6 +20525,10 @@ bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */) return true; } +/** + * @brief Allow to walk on water. Doesn't inform the client. + * Need to use SendMovementWaterWalking() if it's for players. + */ bool Unit::SetWaterWalking(bool enable, bool /*packetOnly = false*/) { if (enable == HasUnitMovementFlag(MOVEMENTFLAG_WATERWALKING)) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 97efd9c2f..4e7fc51d1 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -717,6 +717,7 @@ public: return nullptr; } bool Attack(Unit* victim, bool meleeAttack); + void CastStop(uint32 except_spellid = 0, bool withInstant = true); bool AttackStop(); void RemoveAllAttackers(); @@ -821,10 +822,10 @@ public: void SetUInt32Value(uint16 index, uint32 value); UnitFlags GetUnitFlags() const { return UnitFlags(GetUInt32Value(UNIT_FIELD_FLAGS)); } - bool HasUnitFlag(UnitFlags flags) const { return HasFlag(UNIT_FIELD_FLAGS, flags); } - void SetUnitFlag(UnitFlags flags) { SetFlag(UNIT_FIELD_FLAGS, flags); } - void RemoveUnitFlag(UnitFlags flags) { RemoveFlag(UNIT_FIELD_FLAGS, flags); } - void ReplaceAllUnitFlags(UnitFlags flags) { SetUInt32Value(UNIT_FIELD_FLAGS, flags); } + bool HasUnitFlag(UnitFlags flags) const { return HasFlag(UNIT_FIELD_FLAGS, flags); } /// @brief UnitFlags available in UnitDefines.h + void SetUnitFlag(UnitFlags flags) { SetFlag(UNIT_FIELD_FLAGS, flags); } /// @brief UnitFlags available in UnitDefines.h + void RemoveUnitFlag(UnitFlags flags) { RemoveFlag(UNIT_FIELD_FLAGS, flags); } /// @brief Remove the Unit flag specify only + void ReplaceAllUnitFlags(UnitFlags flags) { SetUInt32Value(UNIT_FIELD_FLAGS, flags); } /// @brief Remove all UnitFlags and set new ones. UnitFlags available in UnitDefines.h UnitFlags2 GetUnitFlags2() const { return UnitFlags2(GetUInt32Value(UNIT_FIELD_FLAGS_2)); } bool HasUnitFlag2(UnitFlags2 flags) const { return HasFlag(UNIT_FIELD_FLAGS_2, flags); } @@ -1607,7 +1608,7 @@ public: [[nodiscard]] bool IsStopped() const { return !(HasUnitState(UNIT_STATE_MOVING)); } void StopMoving(); - void StopMovingOnCurrentPos(); + void StopMovingOnCurrentPos(); /// @brief Disable the unit movement by clearing UNIT_STATE_MOVING and stopping the spline. virtual void PauseMovement(uint32 timer = 0, uint8 slot = 0); // timer in ms void ResumeMovement(uint32 timer = 0, uint8 slot = 0); diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 8948c0d46..84167be22 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -236,9 +236,11 @@ void MotionMaster::MoveIdle() Mutate(GetIdleMovementGenerator(), MOTION_SLOT_IDLE); } +/** + * @brief Enable a random movement in desired range around the unit. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveRandom(float wanderDistance) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -249,6 +251,11 @@ void MotionMaster::MoveRandom(float wanderDistance) } } +/** + * @brief The unit will return this initial position (owner for pets and summoned creatures). Doesn't work with UNIT_FLAG_DISABLE_MOVE + * + * @param walk The unit will run by default, but you can set it to walk + */ void MotionMaster::MoveTargetedHome(bool walk /*= false*/) { Clear(false); @@ -261,7 +268,7 @@ void MotionMaster::MoveTargetedHome(bool walk /*= false*/) else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID()) { _owner->ClearUnitState(UNIT_STATE_EVADE); - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE + if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -279,6 +286,9 @@ void MotionMaster::MoveTargetedHome(bool walk /*= false*/) } } +/** + * @brief Enable the confusion movement. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveConfused() { // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE @@ -297,9 +307,11 @@ void MotionMaster::MoveConfused() } } +/** + * @brief Force the unit to chase this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveChase(Unit* target, std::optional dist, std::optional angle) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE // ignore movement request if target not exist if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -391,9 +403,11 @@ void MotionMaster::MoveCircleTarget(Unit* target) init.Launch(); } +/** + * @brief The unit will follow this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot, bool inheritWalkState) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE // ignore movement request if target not exist if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) { @@ -415,9 +429,13 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo } } +/** + * @brief The unit will move to a specific point. Doesn't work with UNIT_FLAG_DISABLE_MOVE + * + * For transition movement between the ground and the air, use MoveLand or MoveTakeoff instead. + */ void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generatePath, bool forceDestination, MovementSlot slot, float orientation /* = 0.0f*/) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -464,9 +482,11 @@ void MotionMaster::MoveSplinePath(uint32 path_id) MoveSplinePath(points); } +/** + * @brief Use to move the unit from the air to the ground. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveLand(uint32 id, Position const& pos, float speed /* = 0.0f*/) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -488,15 +508,20 @@ void MotionMaster::MoveLand(uint32 id, Position const& pos, float speed /* = 0.0 Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE); } +/** + * @brief Use to move the unit from the air to the ground. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveLand(uint32 id, float x, float y, float z, float speed /* = 0.0f*/) { Position pos = {x, y, z, 0.0f}; MoveLand(id, pos, speed); } +/** + * @brief Use to move the unit from the ground to the air. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos, float speed /* = 0.0f*/) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -518,6 +543,9 @@ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos, float speed /* = Mutate(new EffectMovementGenerator(id), MOTION_SLOT_ACTIVE); } +/** + * @brief Use to move the unit from the air to the ground. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveTakeoff(uint32 id, float x, float y, float z, float speed /* = 0.0f*/) { Position pos = {x, y, z, 0.0f}; @@ -550,6 +578,9 @@ void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, floa Mutate(new EffectMovementGenerator(0), MOTION_SLOT_CONTROLLED); } +/** + * @brief The unit will jump in a specific direction + */ void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ) { //this function may make players fall below map @@ -564,6 +595,9 @@ void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ) MoveJump(x, y, z, speedXY, speedZ); } +/** + * @brief The unit will jump to a specific point + */ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id, Unit const* target) { LOG_DEBUG("movement.motionmaster", "Unit ({}) jump to point (X: {} Y: {} Z: {})", _owner->GetGUID().ToString(), x, y, z); @@ -584,9 +618,11 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED); } +/** + * @brief The unit will fall. Used when in the air. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveFall(uint32 id /*=0*/, bool addFlagForNPC) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -625,9 +661,11 @@ void MotionMaster::MoveFall(uint32 id /*=0*/, bool addFlagForNPC) Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED); } +/** + * @brief The unit will charge the target. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, const Movement::PointsArray* path, bool generatePath, float orientation /* = 0.0f*/, ObjectGuid targetGUID /*= ObjectGuid::Empty*/) { - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -646,6 +684,9 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id, } } +/** + * @brief The unit will charge the target. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveCharge(PathGenerator const& path, float speed /*= SPEED_CHARGE*/, ObjectGuid targetGUID /*= ObjectGuid::Empty*/) { G3D::Vector3 dest = path.GetActualEndPosition(); @@ -696,12 +737,14 @@ void MotionMaster::MoveSeekAssistanceDistract(uint32 time) } } +/** + * @brief Enable the target's fleeing movement. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MoveFleeing(Unit* enemy, uint32 time) { if (!enemy) return; - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -745,12 +788,15 @@ void MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode) } } +/** + * @brief Enable the target's distract movement. Doesn't work with UNIT_FLAG_DISABLE_MOVE and + * if the unit has MOTION_SLOT_CONTROLLED (generaly apply when the unit is controlled). + */ void MotionMaster::MoveDistract(uint32 timer) { if (Impl[MOTION_SLOT_CONTROLLED]) return; - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -773,7 +819,7 @@ void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot) { bool delayed = (_top == slot && (_cleanFlag & MMCF_UPDATE)); - // pussywizard: clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete + // clear slot AND decrease top immediately to avoid crashes when referencing null top in DirectDelete Impl[slot] = nullptr; while (!empty() && !top()) --_top; @@ -797,12 +843,14 @@ void MotionMaster::Mutate(MovementGenerator* m, MovementSlot slot) } } +/** + * @brief Move the unit following a specific path. Doesn't work with UNIT_FLAG_DISABLE_MOVE + */ void MotionMaster::MovePath(uint32 path_id, bool repeatable) { if (!path_id) return; - // Xinef: do not allow to move with UNIT_FLAG_DISABLE_MOVE if (_owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE)) return; @@ -825,6 +873,9 @@ void MotionMaster::MovePath(uint32 path_id, bool repeatable) _owner->IsPlayer() ? "Player" : "Creature", _owner->GetGUID().ToString(), path_id, repeatable ? "YES" : "NO"); } +/** + * @brief Rotate the unit. You can specify the time of the rotation. + */ void MotionMaster::MoveRotate(uint32 time, RotateDirection direction) { if (!time)