aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-01-08 22:23:12 +0100
committerShauren <shauren.trinity@gmail.com>2024-01-08 22:23:12 +0100
commit605e5f94c0d71cad8e83fa5a07eaec4e6bed9cc3 (patch)
tree5cad677458a22ef0dd187fa86d1cd574282384dd /src/server/game
parenteeb4407f077bf567361becdbe5083c2790f00313 (diff)
Core/Creatures: Moved autoattack handling from scripts to game
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/AI/CoreAI/CombatAI.cpp9
-rw-r--r--src/server/game/AI/CoreAI/GuardAI.cpp5
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.cpp4
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp9
-rw-r--r--src/server/game/AI/CoreAI/ReactorAI.cpp5
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp35
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h1
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp9
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp5
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp5
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp10
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp2
-rw-r--r--src/server/game/Entities/Player/Player.cpp95
-rw-r--r--src/server/game/Entities/Player/Player.h7
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp96
-rw-r--r--src/server/game/Entities/Unit/Unit.h5
-rw-r--r--src/server/game/Entities/Unit/UnitDefines.h8
-rw-r--r--src/server/game/Server/Packets/CombatPackets.cpp2
-rw-r--r--src/server/game/Server/Packets/CombatPackets.h11
20 files changed, 110 insertions, 215 deletions
diff --git a/src/server/game/AI/CoreAI/CombatAI.cpp b/src/server/game/AI/CoreAI/CombatAI.cpp
index 16c161dbd7a..b20cf471b0c 100644
--- a/src/server/game/AI/CoreAI/CombatAI.cpp
+++ b/src/server/game/AI/CoreAI/CombatAI.cpp
@@ -43,10 +43,7 @@ int32 AggressorAI::Permissible(Creature const* creature)
void AggressorAI::UpdateAI(uint32 /*diff*/)
{
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
+ UpdateVictim();
}
/////////////////
@@ -107,8 +104,6 @@ void CombatAI::UpdateAI(uint32 diff)
if (AISpellInfoType const* info = GetAISpellInfo(spellId, me->GetMap()->GetDifficultyID()))
_events.ScheduleEvent(spellId, info->cooldown, info->cooldown * 2);
}
- else
- DoMeleeAttackIfReady();
}
void CombatAI::SpellInterrupted(uint32 spellId, uint32 unTimeMs)
@@ -202,6 +197,7 @@ TurretAI::TurretAI(Creature* creature, uint32 scriptId) : CreatureAI(creature, s
_minimumRange = spellInfo ? spellInfo->GetMinRange(false) : 0;
creature->m_CombatDistance = spellInfo ? spellInfo->GetMaxRange(false) : 0;
creature->m_SightDistance = creature->m_CombatDistance;
+ creature->SetCanMelee(false);
}
bool TurretAI::CanAIAttack(Unit const* who) const
@@ -235,6 +231,7 @@ VehicleAI::VehicleAI(Creature* creature, uint32 scriptId) : CreatureAI(creature,
LoadConditions();
_dismiss = false;
_dismissTimer = VEHICLE_DISMISS_TIME;
+ me->SetCanMelee(false);
}
// NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted
diff --git a/src/server/game/AI/CoreAI/GuardAI.cpp b/src/server/game/AI/CoreAI/GuardAI.cpp
index 5d6b62e83a9..21511c1f2c0 100644
--- a/src/server/game/AI/CoreAI/GuardAI.cpp
+++ b/src/server/game/AI/CoreAI/GuardAI.cpp
@@ -31,10 +31,7 @@ int32 GuardAI::Permissible(Creature const* creature)
void GuardAI::UpdateAI(uint32 /*diff*/)
{
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
+ UpdateVictim();
}
bool GuardAI::CanSeeAlways(WorldObject const* obj)
diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp
index 8bae96a6092..82904e5f52f 100644
--- a/src/server/game/AI/CoreAI/PassiveAI.cpp
+++ b/src/server/game/AI/CoreAI/PassiveAI.cpp
@@ -22,6 +22,7 @@
PassiveAI::PassiveAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId)
{
me->SetReactState(REACT_PASSIVE);
+ me->SetCanMelee(false);
}
PossessedAI::PossessedAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId)
@@ -32,6 +33,7 @@ PossessedAI::PossessedAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId)
NullCreatureAI::NullCreatureAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId)
{
me->SetReactState(REACT_PASSIVE);
+ me->SetCanMelee(false);
}
int32 NullCreatureAI::Permissible(Creature const* creature)
@@ -62,8 +64,6 @@ void PossessedAI::UpdateAI(uint32 /*diff*/)
{
if (!me->IsValidAttackTarget(me->GetVictim()))
me->AttackStop();
- else
- DoMeleeAttackIfReady();
}
}
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp
index 42666d2a1e2..16e9356f646 100644
--- a/src/server/game/AI/CoreAI/PetAI.cpp
+++ b/src/server/game/AI/CoreAI/PetAI.cpp
@@ -80,15 +80,6 @@ void PetAI::UpdateAI(uint32 diff)
StopAttack();
return;
}
-
- // Check before attacking to prevent pets from leaving stay position
- if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
- {
- if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->GetVictim())))
- DoMeleeAttackIfReady();
- }
- else
- DoMeleeAttackIfReady();
}
else
{
diff --git a/src/server/game/AI/CoreAI/ReactorAI.cpp b/src/server/game/AI/CoreAI/ReactorAI.cpp
index 8b15bed1908..a22876a067b 100644
--- a/src/server/game/AI/CoreAI/ReactorAI.cpp
+++ b/src/server/game/AI/CoreAI/ReactorAI.cpp
@@ -28,8 +28,5 @@ int32 ReactorAI::Permissible(Creature const* creature)
void ReactorAI::UpdateAI(uint32 /*diff*/)
{
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
+ UpdateVictim();
}
diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index c8d2f61e77b..1470ca07bd9 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -58,41 +58,6 @@ void UnitAI::AttackStartCaster(Unit* victim, float dist)
me->GetMotionMaster()->MoveChase(victim, dist);
}
-void UnitAI::DoMeleeAttackIfReady()
-{
- if (me->IsCreature() && !me->ToCreature()->CanMelee())
- return;
-
- if (me->HasUnitState(UNIT_STATE_CASTING))
- {
- Spell* channeledSpell = me->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
- if (!channeledSpell || !channeledSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_ALLOW_ACTIONS_DURING_CHANNEL))
- return;
- }
-
- Unit* victim = me->GetVictim();
-
- if (!me->IsWithinMeleeRange(victim))
- return;
-
- // Check that the victim is in front of the unit
- if (!me->HasInArc(2 * float(M_PI) / 3, victim))
- return;
-
- //Make sure our attack is ready and we aren't currently casting before checking distance
- if (me->isAttackReady())
- {
- me->AttackerStateUpdate(victim);
- me->resetAttackTimer();
- }
-
- if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK))
- {
- me->AttackerStateUpdate(victim, OFF_ATTACK);
- me->resetAttackTimer(OFF_ATTACK);
- }
-}
-
bool UnitAI::DoSpellAttackIfReady(uint32 spellId)
{
if (me->HasUnitState(UNIT_STATE_CASTING) || !me->isAttackReady())
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index fed0cae7203..1ae52ff00a7 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -160,7 +160,6 @@ class TC_GAME_API UnitAI
SpellCastResult DoCastVictim(uint32 spellId, CastSpellExtraArgs const& args = {});
SpellCastResult DoCastAOE(uint32 spellId, CastSpellExtraArgs const& args = {}) { return DoCast(nullptr, spellId, args); }
- void DoMeleeAttackIfReady();
bool DoSpellAttackIfReady(uint32 spellId);
static std::unordered_map<std::pair<uint32, Difficulty>, AISpellInfoType> AISpellInfo;
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp
index 2f104c40eac..bfa4ea84f96 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.cpp
+++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp
@@ -591,8 +591,6 @@ void PlayerAI::DoAutoAttackIfReady()
{
if (IsRangedAttacker())
DoRangedAttackIfReady();
- else
- DoMeleeAttackIfReady();
}
void PlayerAI::CancelAllShapeshifts()
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 00c15509f5a..82c8dac896f 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -149,10 +149,7 @@ void ScriptedAI::AttackStart(Unit* who)
void ScriptedAI::UpdateAI(uint32 /*diff*/)
{
// Check if we have a current target
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
+ UpdateVictim();
}
void ScriptedAI::DoStartMovement(Unit* victim, float distance, float angle)
@@ -629,8 +626,6 @@ void BossAI::UpdateAI(uint32 diff)
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
-
- DoMeleeAttackIfReady();
}
bool BossAI::CanAIAttack(Unit const* target) const
@@ -716,6 +711,4 @@ void WorldBossAI::UpdateAI(uint32 diff)
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
-
- DoMeleeAttackIfReady();
}
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index ca6290d1379..19d11c4b957 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -248,10 +248,7 @@ void EscortAI::UpdateAI(uint32 diff)
void EscortAI::UpdateEscortAI(uint32 /*diff*/)
{
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
+ UpdateVictim();
}
void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, bool run)
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index f12b7aaf9b6..c0a5aa3350a 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -147,10 +147,7 @@ void FollowerAI::UpdateAI(uint32 uiDiff)
void FollowerAI::UpdateFollowerAI(uint32 /*uiDiff*/)
{
- if (!UpdateVictim())
- return;
-
- DoMeleeAttackIfReady();
+ UpdateVictim();
}
void FollowerAI::StartFollow(Player* player, uint32 factionForFollower, uint32 quest)
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 370e2d1a9f8..9177443d57f 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -283,21 +283,13 @@ void SmartAI::UpdateAI(uint32 diff)
CheckConditions(diff);
- bool hasVictim = UpdateVictim();
+ UpdateVictim();
GetScript()->OnUpdate(diff);
UpdatePath(diff);
UpdateFollow(diff);
UpdateDespawn(diff);
-
- if (!IsAIControlled())
- return;
-
- if (!hasVictim)
- return;
-
- DoMeleeAttackIfReady();
}
bool SmartAI::IsEscortInvokerInRange()
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index f690c8e7727..86a31159b61 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -852,6 +852,8 @@ void Creature::Update(uint32 diff)
Unit::AIUpdateTick(diff);
+ DoMeleeAttackIfReady();
+
// creature can be dead after UpdateAI call
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
if (!IsAlive())
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 642d377d7dd..908f2c56863 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -235,8 +235,6 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
m_deathTimer = 0;
m_deathExpireTime = 0;
- m_swingErrorMsg = 0;
-
for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
{
m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
@@ -1011,78 +1009,7 @@ void Player::Update(uint32 p_time)
m_achievementMgr->UpdateTimedCriteria(Milliseconds(p_time));
- if (HasUnitState(UNIT_STATE_MELEE_ATTACKING) && !HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_CHARGING))
- {
- if (Unit* victim = GetVictim())
- {
- // default combat reach 10
- /// @todo add weapon, skill check
-
- if (isAttackReady(BASE_ATTACK))
- {
- if (!IsWithinMeleeRange(victim))
- {
- setAttackTimer(BASE_ATTACK, 100);
- if (m_swingErrorMsg != 1) // send single time (client auto repeat)
- {
- SendAttackSwingNotInRange();
- m_swingErrorMsg = 1;
- }
- }
- //120 degrees of radiant range, if player is not in boundary radius
- else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
- {
- setAttackTimer(BASE_ATTACK, 100);
- if (m_swingErrorMsg != 2) // send single time (client auto repeat)
- {
- SendAttackSwingBadFacingAttack();
- m_swingErrorMsg = 2;
- }
- }
- else
- {
- m_swingErrorMsg = 0; // reset swing error state
-
- // prevent base and off attack in same time, delay attack at 0.2 sec
- if (haveOffhandWeapon())
- if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
- setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
-
- // do attack
- AttackerStateUpdate(victim, BASE_ATTACK);
- resetAttackTimer(BASE_ATTACK);
- }
- }
-
- if (!IsInFeralForm() && haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
- {
- if (!IsWithinMeleeRange(victim))
- setAttackTimer(OFF_ATTACK, 100);
- else if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
- {
- setAttackTimer(BASE_ATTACK, 100);
- }
- else
- {
- // prevent base and off attack in same time, delay attack at 0.2 sec
- if (getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
- setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);
-
- // do attack
- AttackerStateUpdate(victim, OFF_ATTACK);
- resetAttackTimer(OFF_ATTACK);
- }
- }
-
- /*Unit* owner = victim->GetOwner();
- Unit* u = owner ? owner : victim;
- if (u->IsPvP() && (!duel || duel->opponent != u))
- {
- UpdatePvP(true);
- RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::PvPActive);
- }*/
- }
- }
+ DoMeleeAttackIfReady();
if (HasPlayerFlag(PLAYER_FLAGS_RESTING))
_restMgr->Update(now);
@@ -21308,29 +21235,17 @@ void Player::SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGui
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
-void Player::SendAttackSwingCantAttack() const
-{
- SendDirectMessage(WorldPackets::Combat::AttackSwingError(WorldPackets::Combat::AttackSwingError::CantAttack).Write());
-}
-
void Player::SendAttackSwingCancelAttack() const
{
SendDirectMessage(WorldPackets::Combat::CancelCombat().Write());
}
-void Player::SendAttackSwingDeadTarget() const
+void Player::SetAttackSwingError(Optional<AttackSwingErr> err)
{
- SendDirectMessage(WorldPackets::Combat::AttackSwingError(WorldPackets::Combat::AttackSwingError::DeadTarget).Write());
-}
+ if (err && err != m_swingErrorMsg)
+ SendDirectMessage(WorldPackets::Combat::AttackSwingError(*err).Write());
-void Player::SendAttackSwingNotInRange() const
-{
- SendDirectMessage(WorldPackets::Combat::AttackSwingError(WorldPackets::Combat::AttackSwingError::NotInRange).Write());
-}
-
-void Player::SendAttackSwingBadFacingAttack() const
-{
- SendDirectMessage(WorldPackets::Combat::AttackSwingError(WorldPackets::Combat::AttackSwingError::BadFacing).Write());
+ m_swingErrorMsg = err;
}
void Player::SendAutoRepeatCancel(Unit* target)
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e9be541a0e5..0dda39ea498 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -2126,11 +2126,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void DestroyForPlayer(Player* target) const override;
// notifiers
- void SendAttackSwingCantAttack() const;
void SendAttackSwingCancelAttack() const;
- void SendAttackSwingDeadTarget() const;
- void SendAttackSwingNotInRange() const;
- void SendAttackSwingBadFacingAttack() const;
+ void SetAttackSwingError(Optional<AttackSwingErr> err);
void SendAutoRepeatCancel(Unit* target);
void SendExplorationExperience(uint32 Area, uint32 Experience) const;
@@ -3096,7 +3093,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
bool m_canBlock;
bool m_canTitanGrip;
uint32 m_titanGripPenaltySpellId;
- uint8 m_swingErrorMsg;
+ Optional<AttackSwingErr> m_swingErrorMsg;
// Social
PlayerSocial* m_social;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 368926acc5e..84273e7b8c2 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -407,8 +407,8 @@ Unit::~Unit()
ASSERT(m_appliedAuras.empty());
ASSERT(m_ownedAuras.empty());
ASSERT(m_removedAuras.empty());
- ASSERT(m_gameObj.empty());
ASSERT(m_dynObj.empty());
+ ASSERT(m_gameObj.empty());
ASSERT(m_areaTrigger.empty());
ASSERT(!m_unitMovedByMe || (m_unitMovedByMe == this));
ASSERT(!m_playerMovingMe || (m_playerMovingMe == this));
@@ -456,10 +456,10 @@ void Unit::Update(uint32 p_time)
{
if (uint32 base_att = getAttackTimer(BASE_ATTACK))
setAttackTimer(BASE_ATTACK, (p_time >= base_att ? 0 : base_att - p_time));
- if (uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
- setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time));
if (uint32 off_att = getAttackTimer(OFF_ATTACK))
setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_att - p_time));
+ if (uint32 ranged_att = getAttackTimer(RANGED_ATTACK))
+ setAttackTimer(RANGED_ATTACK, (p_time >= ranged_att ? 0 : ranged_att - p_time));
}
// update abilities available only for fraction of time
@@ -2087,12 +2087,15 @@ void Unit::HandleEmoteCommand(Emote emoteId, Player* target /*=nullptr*/, Trinit
}
}
-void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra)
+void Unit::DoMeleeAttackIfReady()
{
- if (HasUnitFlag(UNIT_FLAG_PACIFIED))
+ if (!HasUnitState(UNIT_STATE_MELEE_ATTACKING))
return;
- if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) && !extra)
+ if (HasUnitState(UNIT_STATE_CHARGING))
+ return;
+
+ if (IsCreature() && !ToCreature()->CanMelee())
return;
if (HasUnitState(UNIT_STATE_CASTING))
@@ -2102,6 +2105,69 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr
return;
}
+ Unit* victim = GetVictim();
+ if (!victim)
+ return;
+
+ auto getAutoAttackError = [&]() -> Optional<AttackSwingErr>
+ {
+ if (!IsWithinMeleeRange(victim))
+ return AttackSwingErr::NotInRange;
+
+ //120 degrees of radiant range, if player is not in boundary radius
+ if (!IsWithinBoundaryRadius(victim) && !HasInArc(2 * float(M_PI) / 3, victim))
+ return AttackSwingErr::BadFacing;
+
+ return {};
+ };
+
+ if (isAttackReady(BASE_ATTACK))
+ {
+ Optional<AttackSwingErr> autoAttackError = getAutoAttackError();
+ if (!autoAttackError)
+ {
+ // prevent base and off attack in same time, delay attack at 0.2 sec
+ if (haveOffhandWeapon())
+ if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
+ setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
+
+ // do attack
+ AttackerStateUpdate(victim, BASE_ATTACK);
+ resetAttackTimer(BASE_ATTACK);
+ }
+ else
+ setAttackTimer(BASE_ATTACK, 100);
+
+ if (Player* attackerPlayer = ToPlayer())
+ attackerPlayer->SetAttackSwingError(autoAttackError);
+ }
+
+ if (!IsInFeralForm() && haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
+ {
+ Optional<AttackSwingErr> autoAttackError = getAutoAttackError();
+ if (!autoAttackError)
+ {
+ // prevent base and off attack in same time, delay attack at 0.2 sec
+ if (getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
+ setAttackTimer(BASE_ATTACK, ATTACK_DISPLAY_DELAY);
+
+ // do attack
+ AttackerStateUpdate(victim, OFF_ATTACK);
+ resetAttackTimer(OFF_ATTACK);
+ }
+ else
+ setAttackTimer(OFF_ATTACK, 100);
+ }
+}
+
+void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extra)
+{
+ if (HasUnitFlag(UNIT_FLAG_PACIFIED))
+ return;
+
+ if (HasUnitState(UNIT_STATE_CANNOT_AUTOATTACK) && !extra)
+ return;
+
if (HasAuraType(SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES))
return;
@@ -5070,7 +5136,7 @@ void Unit::_RegisterDynObject(DynamicObject* dynObj)
void Unit::_UnregisterDynObject(DynamicObject* dynObj)
{
- m_dynObj.remove(dynObj);
+ std::erase(m_dynObj, dynObj);
if (GetTypeId() == TYPEID_UNIT && IsAIEnabled())
ToCreature()->AI()->JustUnregisteredDynObject(dynObj);
}
@@ -5093,8 +5159,6 @@ std::vector<DynamicObject*> Unit::GetDynObjects(uint32 spellId) const
void Unit::RemoveDynObject(uint32 spellId)
{
- if (m_dynObj.empty())
- return;
for (DynObjectList::iterator i = m_dynObj.begin(); i != m_dynObj.end();)
{
DynamicObject* dynObj = *i;
@@ -5111,7 +5175,7 @@ void Unit::RemoveDynObject(uint32 spellId)
void Unit::RemoveAllDynObjects()
{
while (!m_dynObj.empty())
- m_dynObj.front()->Remove();
+ m_dynObj.back()->Remove();
}
GameObject* Unit::GetGameObject(uint32 spellId) const
@@ -5598,14 +5662,8 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
Creature* creature = ToCreature();
// creatures cannot attack while evading
- if (creature)
- {
- if (creature->IsInEvadeMode())
- return false;
-
- if (!creature->CanMelee())
- meleeAttack = false;
- }
+ if (creature && creature->IsInEvadeMode())
+ return false;
// nobody can attack GM in GM-mode
if (victim->GetTypeId() == TYPEID_PLAYER)
@@ -11022,7 +11080,6 @@ void Unit::SetControlled(bool apply, UnitState state)
case UNIT_STATE_CONFUSED:
if (!HasUnitState(UNIT_STATE_STUNNED))
{
- ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
SendMeleeAttackStop();
// SendAutoRepeatCancel ?
SetConfused(true);
@@ -11031,7 +11088,6 @@ void Unit::SetControlled(bool apply, UnitState state)
case UNIT_STATE_FLEEING:
if (!HasUnitState(UNIT_STATE_STUNNED | UNIT_STATE_CONFUSED))
{
- ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
SendMeleeAttackStop();
// SendAutoRepeatCancel ?
SetFeared(true);
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 08beac15956..70124a92542 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1053,7 +1053,8 @@ class TC_GAME_API Unit : public WorldObject
void TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProcContainer& procAuras);
void HandleEmoteCommand(Emote emoteId, Player* target = nullptr, Trinity::IteratorPair<int32 const*> spellVisualKitIds = {}, int32 sequenceVariation = 0);
- void AttackerStateUpdate (Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false);
+ void DoMeleeAttackIfReady();
+ void AttackerStateUpdate(Unit* victim, WeaponAttackType attType = BASE_ATTACK, bool extra = false);
void CalculateMeleeDamage(Unit* victim, CalcDamageInfo* damageInfo, WeaponAttackType attackType = BASE_ATTACK);
void DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss);
@@ -1957,7 +1958,7 @@ class TC_GAME_API Unit : public WorldObject
int32 m_procDeep; // tracked for proc system correctness (what spells should proc what)
int32 m_procChainLength; // tracked to protect against infinite proc loops (hard limit, will disallow procs even if they should happen)
- typedef std::list<DynamicObject*> DynObjectList;
+ typedef std::vector<DynamicObject*> DynObjectList;
DynObjectList m_dynObj;
typedef std::list<GameObject*> GameObjectList;
diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h
index 92d23e7a8a9..348a9f6d611 100644
--- a/src/server/game/Entities/Unit/UnitDefines.h
+++ b/src/server/game/Entities/Unit/UnitDefines.h
@@ -473,6 +473,14 @@ enum HitInfo
HITINFO_FAKE_DAMAGE = 0x01000000 // enables damage animation even if no damage done, set only if no damage
};
+enum class AttackSwingErr : uint8
+{
+ NotInRange = 0,
+ BadFacing = 1,
+ CantAttack = 2,
+ DeadTarget = 3
+};
+
#define MAX_DECLINED_NAME_CASES 5
struct TC_GAME_API DeclinedName
diff --git a/src/server/game/Server/Packets/CombatPackets.cpp b/src/server/game/Server/Packets/CombatPackets.cpp
index d662128556f..33dd2c9a64a 100644
--- a/src/server/game/Server/Packets/CombatPackets.cpp
+++ b/src/server/game/Server/Packets/CombatPackets.cpp
@@ -96,7 +96,7 @@ WorldPacket const* WorldPackets::Combat::AIReaction::Write()
WorldPacket const* WorldPackets::Combat::AttackSwingError::Write()
{
- _worldPacket.WriteBits(Reason, 3);
+ _worldPacket.WriteBits(AsUnderlyingType(Reason), 3);
_worldPacket.FlushBits();
return &_worldPacket;
}
diff --git a/src/server/game/Server/Packets/CombatPackets.h b/src/server/game/Server/Packets/CombatPackets.h
index 4aaa57ceba9..27dc7ec44ad 100644
--- a/src/server/game/Server/Packets/CombatPackets.h
+++ b/src/server/game/Server/Packets/CombatPackets.h
@@ -20,6 +20,7 @@
#include "Packet.h"
#include "ObjectGuid.h"
+#include "UnitDefines.h"
class Unit;
enum Powers : int8;
@@ -41,20 +42,12 @@ namespace WorldPackets
class AttackSwingError final : public ServerPacket
{
public:
- enum AttackSwingErr : uint8
- {
- NotInRange = 0,
- BadFacing = 1,
- CantAttack = 2,
- DeadTarget = 3
- };
-
AttackSwingError() : ServerPacket(SMSG_ATTACK_SWING_ERROR, 4) { }
AttackSwingError(AttackSwingErr reason) : ServerPacket(SMSG_ATTACK_SWING_ERROR, 4), Reason(reason) { }
WorldPacket const* Write() override;
- AttackSwingErr Reason = CantAttack;
+ AttackSwingErr Reason = AttackSwingErr::CantAttack;
};
class AttackStop final : public ClientPacket