From 8e68d1bcaf96f1689e056ecef014949dfeb1c1da Mon Sep 17 00:00:00 2001 From: Schmoozerd Date: Thu, 3 Feb 2011 00:35:53 +0300 Subject: [PATCH] [11105] Melee attacks distance Inspired by patch provided by Feanordev. Signed-off-by: VladimirMangos --- src/game/AggressorAI.cpp | 2 +- src/game/CreatureEventAI.cpp | 2 +- src/game/GuardAI.cpp | 2 +- src/game/PetAI.cpp | 11 +++++---- src/game/Player.cpp | 24 +++++++++---------- src/game/ReactorAI.cpp | 6 ++--- src/game/SharedDefines.h | 7 ++++++ src/game/Spell.cpp | 33 +++++++++++++++++++------- src/game/TargetedMovementGenerator.cpp | 2 +- src/game/ThreatManager.cpp | 6 ++--- src/game/Unit.cpp | 20 ++++++++++++---- src/game/Unit.h | 3 ++- src/shared/revision_nr.h | 2 +- 13 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp index 74aa7d3e5..f479df01b 100644 --- a/src/game/AggressorAI.cpp +++ b/src/game/AggressorAI.cpp @@ -129,7 +129,7 @@ AggressorAI::UpdateAI(const uint32 /*diff*/) if( m_creature->isAttackReady() ) { - if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp index 6409c6e4f..670901cf8 100644 --- a/src/game/CreatureEventAI.cpp +++ b/src/game/CreatureEventAI.cpp @@ -1370,7 +1370,7 @@ void CreatureEventAI::DoMeleeAttackIfReady() if (m_creature->isAttackReady()) { //If we are within range melee the target - if (m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); diff --git a/src/game/GuardAI.cpp b/src/game/GuardAI.cpp index ce586d73f..0385c890b 100644 --- a/src/game/GuardAI.cpp +++ b/src/game/GuardAI.cpp @@ -115,7 +115,7 @@ void GuardAI::UpdateAI(const uint32 /*diff*/) if (m_creature->isAttackReady()) { - if (m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index 480c973b9..0c4a99621 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -145,7 +145,10 @@ void PetAI::UpdateAI(const uint32 diff) _stopAttack(); return; } - else if (m_creature->IsStopped() || m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + + bool meleeReach = m_creature->CanReachWithMeleeAttack(m_creature->getVictim()); + + if (m_creature->IsStopped() || meleeReach) { // required to be stopped cases if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false)) @@ -156,7 +159,7 @@ void PetAI::UpdateAI(const uint32 diff) return; } // not required to be stopped case - else if (m_creature->isAttackReady() && m_creature->canReachWithAttack(m_creature->getVictim())) + else if (m_creature->isAttackReady() && meleeReach) { m_creature->AttackerStateUpdate(m_creature->getVictim()); @@ -168,7 +171,7 @@ void PetAI::UpdateAI(const uint32 diff) //if pet misses its target, it will also be the first in threat list m_creature->getVictim()->AddThreat(m_creature); - if( _needToStop() ) + if (_needToStop()) _stopAttack(); } } @@ -350,6 +353,6 @@ void PetAI::AttackedBy(Unit *attacker) { //when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway if(!m_creature->getVictim() && m_creature->GetCharmInfo() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) && - (!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->canReachWithAttack(attacker))) + (!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->CanReachWithMeleeAttack(attacker))) AttackStart(attacker); } diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 90e21b7cf..df1be455c 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -1269,24 +1269,22 @@ void Player::Update( uint32 update_diff, uint32 p_time ) // default combat reach 10 // TODO add weapon,skill check - float pldistance = ATTACK_DISTANCE; - if (isAttackReady(BASE_ATTACK)) { - if(!IsWithinDistInMap(pVictim, pldistance)) + if (!CanReachWithMeleeAttack(pVictim)) { setAttackTimer(BASE_ATTACK,100); - if(m_swingErrorMsg != 1) // send single time (client auto repeat) + if (m_swingErrorMsg != 1) // send single time (client auto repeat) { SendAttackSwingNotInRange(); m_swingErrorMsg = 1; } } //120 degrees of radiant range - else if( !HasInArc( 2*M_PI_F/3, pVictim )) + else if (!HasInArc(2*M_PI_F/3, pVictim)) { setAttackTimer(BASE_ATTACK,100); - if(m_swingErrorMsg != 2) // send single time (client auto repeat) + if (m_swingErrorMsg != 2) // send single time (client auto repeat) { SendAttackSwingBadFacingAttack(); m_swingErrorMsg = 2; @@ -1297,7 +1295,7 @@ void Player::Update( uint32 update_diff, uint32 p_time ) m_swingErrorMsg = 0; // reset swing error state // prevent base and off attack in same time, delay attack at 0.2 sec - if(haveOffhandWeapon()) + if (haveOffhandWeapon()) { uint32 off_att = getAttackTimer(OFF_ATTACK); if(off_att < ATTACK_DISPLAY_DELAY) @@ -1308,13 +1306,13 @@ void Player::Update( uint32 update_diff, uint32 p_time ) } } - if ( haveOffhandWeapon() && isAttackReady(OFF_ATTACK)) + if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK)) { - if(!IsWithinDistInMap(pVictim, pldistance)) + if (!CanReachWithMeleeAttack(pVictim)) { setAttackTimer(OFF_ATTACK,100); } - else if( !HasInArc( 2*M_PI_F/3, pVictim )) + else if (!HasInArc(2*M_PI_F/3, pVictim)) { setAttackTimer(OFF_ATTACK,100); } @@ -1332,7 +1330,7 @@ void Player::Update( uint32 update_diff, uint32 p_time ) Unit *owner = pVictim->GetOwner(); Unit *u = owner ? owner : pVictim; - if(u->IsPvP() && (!duel || duel->opponent != u)) + if (u->IsPvP() && (!duel || duel->opponent != u)) { UpdatePvP(true); RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); @@ -1340,9 +1338,9 @@ void Player::Update( uint32 update_diff, uint32 p_time ) } } - if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) + if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING)) { - if(roll_chance_i(3) && GetTimeInnEnter() > 0) //freeze update + if (roll_chance_i(3) && GetTimeInnEnter() > 0) //freeze update { time_t time_inn = time(NULL)-GetTimeInnEnter(); if (time_inn >= 10) //freeze update diff --git a/src/game/ReactorAI.cpp b/src/game/ReactorAI.cpp index 01cc34f22..4de7d1e96 100644 --- a/src/game/ReactorAI.cpp +++ b/src/game/ReactorAI.cpp @@ -68,14 +68,14 @@ void ReactorAI::UpdateAI(const uint32 /*time_diff*/) { // update i_victimGuid if i_creature.getVictim() !=0 and changed - if(!m_creature->SelectHostileTarget() || !m_creature->getVictim()) + if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) return; i_victimGuid = m_creature->getVictim()->GetGUID(); - if( m_creature->isAttackReady() ) + if (m_creature->isAttackReady()) { - if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + if (m_creature->CanReachWithMeleeAttack(m_creature->getVictim())) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index ee96ecf92..5a045b31d 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -1157,6 +1157,13 @@ enum SpellPreventionType SPELL_PREVENTION_TYPE_PACIFY = 2 }; +// indexes from SpellRange.dbc, listed only special and used in code +enum SpellRangeIndex +{ + SPELL_RANGE_IDX_SELF_ONLY = 1, // 0.0 + SPELL_RANGE_IDX_COMBAT = 2, // 5.5 (but dynamic) +}; + enum DamageEffectType { DIRECT_DAMAGE = 0, // used for normal weapon damage (not for class abilities or spells) diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index f25d5f495..4dd16f56b 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -5711,19 +5711,33 @@ bool Spell::CanAutoCast(Unit* target) SpellCastResult Spell::CheckRange(bool strict) { - float range_mod; + Unit *target = m_targets.getUnitTarget(); - // self cast doesn't need range checking -- also for Starshards fix - if (m_spellInfo->rangeIndex == 1) - return SPELL_CAST_OK; + // special range cases + switch(m_spellInfo->rangeIndex) + { + // self cast doesn't need range checking -- also for Starshards fix + case SPELL_RANGE_IDX_SELF_ONLY: + return SPELL_CAST_OK; + // combat range spells are treated differently + case SPELL_RANGE_IDX_COMBAT: + { + if (target) + { + if (target == m_caster) + return SPELL_CAST_OK; - if (strict) //add radius of caster - range_mod = 1.25; - else //add radius of caster and ~5 yds "give" - range_mod = 6.25; + // with additional 5 dist for non stricted case (some melee spells have delay in apply + return m_caster->CanReachWithMeleeAttack(target, strict ? 0.0f : 5.0f) ? SPELL_CAST_OK : SPELL_FAILED_OUT_OF_RANGE; + } + break; // let continue in generic way for no target + } + } + + //add radius of caster and ~5 yds "give" for non stricred (landing) check + float range_mod = strict ? 1.25f : 6.25; SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); - Unit *target = m_targets.getUnitTarget(); bool friendly = target ? target->IsFriendlyTo(m_caster) : false; float max_range = GetSpellMaxRange(srange, friendly) + range_mod; float min_range = GetSpellMinRange(srange, friendly); @@ -5745,6 +5759,7 @@ SpellCastResult Spell::CheckRange(bool strict) return SPELL_FAILED_UNIT_NOT_INFRONT; } + // TODO verify that such spells really use bounding radius if(m_targets.m_targetMask == TARGET_FLAG_DEST_LOCATION && m_targets.m_destX != 0 && m_targets.m_destY != 0 && m_targets.m_destZ != 0) { if(!m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, max_range)) diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index 80a7f098e..270a6910a 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -193,7 +193,7 @@ bool TargetedMovementGeneratorMedium::Update(T &owner, const uint32 & time_ template void ChaseMovementGenerator::_reachTarget(T &owner) { - if(owner.canReachWithAttack(this->i_target.getTarget())) + if (owner.CanReachWithMeleeAttack(this->i_target.getTarget())) owner.Attack(this->i_target.getTarget(),true); } diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp index 606cd93b7..a1950811e 100644 --- a/src/game/ThreatManager.cpp +++ b/src/game/ThreatManager.cpp @@ -316,9 +316,9 @@ HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, Hostile } } - if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets + if (!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets { - if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target + if (pCurrentVictim) // select 1.3/1.1 better target in comparison current target { // list sorted and and we check current target, then this is best case if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() ) @@ -330,7 +330,7 @@ HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, Hostile if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || (currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && - pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE)) ) + pAttacker->CanReachWithMeleeAttack(target)) ) { //implement 110% threat rule for targets in melee range found = true; //and 130% rule for targets in ranged distances break; //for selecting alive targets diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 4e3b17dd5..22c70599e 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -445,13 +445,23 @@ void Unit::resetAttackTimer(WeaponAttackType type) m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]); } -bool Unit::canReachWithAttack(Unit *pVictim) const +bool Unit::CanReachWithMeleeAttack(Unit* pVictim, float flat_mod /*= 0.0f*/) const { MANGOS_ASSERT(pVictim); - float reach = GetFloatValue(UNIT_FIELD_COMBATREACH); - if( reach <= 0.0f ) - reach = 1.0f; - return IsWithinDistInMap(pVictim, reach); + + // The measured values show BASE_MELEE_OFFSET in (1.3224, 1.342) + float reach = GetFloatValue(UNIT_FIELD_COMBATREACH) + pVictim->GetFloatValue(UNIT_FIELD_COMBATREACH) + + BASE_MELEERANGE_OFFSET + flat_mod; + + if (reach < ATTACK_DISTANCE) + reach = ATTACK_DISTANCE; + + // This check is not related to bounding radius + float dx = GetPositionX() - pVictim->GetPositionX(); + float dy = GetPositionY() - pVictim->GetPositionY(); + float dz = GetPositionZ() - pVictim->GetPositionZ(); + + return dx*dx + dy*dy + dz*dz < reach*reach; } void Unit::RemoveSpellsCausingAura(AuraType auraType) diff --git a/src/game/Unit.h b/src/game/Unit.h index 1a9e203f3..c745c9edf 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -125,6 +125,7 @@ enum SpellFacingFlags SPELL_FACING_FLAG_INFRONT = 0x0001 }; +#define BASE_MELEERANGE_OFFSET 1.33f #define BASE_MINDAMAGE 1.0f #define BASE_MAXDAMAGE 2.0f #define BASE_ATTACK_TIME 2000 @@ -1179,7 +1180,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject return !HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_DISARM_RANGED); } } - bool canReachWithAttack(Unit *pVictim) const; + bool CanReachWithMeleeAttack(Unit* pVictim, float flat_mod = 0.0f) const; uint32 m_extraAttacks; void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*) diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index c45f46f70..b914b85ef 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "11104" + #define REVISION_NR "11105" #endif // __REVISION_NR_H__