[11105] Melee attacks distance

Inspired by patch provided by Feanordev.

Signed-off-by: VladimirMangos <vladimir@getmangos.com>
This commit is contained in:
Schmoozerd 2011-02-03 00:35:53 +03:00 committed by VladimirMangos
parent aa4c1be8cf
commit 8e68d1bcaf
13 changed files with 77 additions and 43 deletions

View file

@ -129,7 +129,7 @@ AggressorAI::UpdateAI(const uint32 /*diff*/)
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->AttackerStateUpdate(m_creature->getVictim());
m_creature->resetAttackTimer(); m_creature->resetAttackTimer();

View file

@ -1370,7 +1370,7 @@ void CreatureEventAI::DoMeleeAttackIfReady()
if (m_creature->isAttackReady()) if (m_creature->isAttackReady())
{ {
//If we are within range melee the target //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->AttackerStateUpdate(m_creature->getVictim());
m_creature->resetAttackTimer(); m_creature->resetAttackTimer();

View file

@ -115,7 +115,7 @@ void GuardAI::UpdateAI(const uint32 /*diff*/)
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->AttackerStateUpdate(m_creature->getVictim());
m_creature->resetAttackTimer(); m_creature->resetAttackTimer();

View file

@ -145,7 +145,10 @@ void PetAI::UpdateAI(const uint32 diff)
_stopAttack(); _stopAttack();
return; 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 // required to be stopped cases
if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false)) if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false))
@ -156,7 +159,7 @@ void PetAI::UpdateAI(const uint32 diff)
return; return;
} }
// not required to be stopped case // 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()); m_creature->AttackerStateUpdate(m_creature->getVictim());
@ -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 //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) && 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); AttackStart(attacker);
} }

View file

@ -1269,11 +1269,9 @@ void Player::Update( uint32 update_diff, uint32 p_time )
// default combat reach 10 // default combat reach 10
// TODO add weapon,skill check // TODO add weapon,skill check
float pldistance = ATTACK_DISTANCE;
if (isAttackReady(BASE_ATTACK)) if (isAttackReady(BASE_ATTACK))
{ {
if(!IsWithinDistInMap(pVictim, pldistance)) if (!CanReachWithMeleeAttack(pVictim))
{ {
setAttackTimer(BASE_ATTACK,100); setAttackTimer(BASE_ATTACK,100);
if (m_swingErrorMsg != 1) // send single time (client auto repeat) if (m_swingErrorMsg != 1) // send single time (client auto repeat)
@ -1310,7 +1308,7 @@ 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); setAttackTimer(OFF_ATTACK,100);
} }

View file

@ -75,7 +75,7 @@ ReactorAI::UpdateAI(const uint32 /*time_diff*/)
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->AttackerStateUpdate(m_creature->getVictim());
m_creature->resetAttackTimer(); m_creature->resetAttackTimer();

View file

@ -1157,6 +1157,13 @@ enum SpellPreventionType
SPELL_PREVENTION_TYPE_PACIFY = 2 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 enum DamageEffectType
{ {
DIRECT_DAMAGE = 0, // used for normal weapon damage (not for class abilities or spells) DIRECT_DAMAGE = 0, // used for normal weapon damage (not for class abilities or spells)

View file

@ -5711,19 +5711,33 @@ bool Spell::CanAutoCast(Unit* target)
SpellCastResult Spell::CheckRange(bool strict) SpellCastResult Spell::CheckRange(bool strict)
{ {
float range_mod; Unit *target = m_targets.getUnitTarget();
// special range cases
switch(m_spellInfo->rangeIndex)
{
// self cast doesn't need range checking -- also for Starshards fix // self cast doesn't need range checking -- also for Starshards fix
if (m_spellInfo->rangeIndex == 1) 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; return SPELL_CAST_OK;
if (strict) //add radius of caster // with additional 5 dist for non stricted case (some melee spells have delay in apply
range_mod = 1.25; return m_caster->CanReachWithMeleeAttack(target, strict ? 0.0f : 5.0f) ? SPELL_CAST_OK : SPELL_FAILED_OUT_OF_RANGE;
else //add radius of caster and ~5 yds "give" }
range_mod = 6.25; 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); SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
Unit *target = m_targets.getUnitTarget();
bool friendly = target ? target->IsFriendlyTo(m_caster) : false; bool friendly = target ? target->IsFriendlyTo(m_caster) : false;
float max_range = GetSpellMaxRange(srange, friendly) + range_mod; float max_range = GetSpellMaxRange(srange, friendly) + range_mod;
float min_range = GetSpellMinRange(srange, friendly); float min_range = GetSpellMinRange(srange, friendly);
@ -5745,6 +5759,7 @@ SpellCastResult Spell::CheckRange(bool strict)
return SPELL_FAILED_UNIT_NOT_INFRONT; 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_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)) if(!m_caster->IsWithinDist3d(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, max_range))

View file

@ -193,7 +193,7 @@ bool TargetedMovementGeneratorMedium<T,D>::Update(T &owner, const uint32 & time_
template<class T> template<class T>
void ChaseMovementGenerator<T>::_reachTarget(T &owner) void ChaseMovementGenerator<T>::_reachTarget(T &owner)
{ {
if(owner.canReachWithAttack(this->i_target.getTarget())) if (owner.CanReachWithMeleeAttack(this->i_target.getTarget()))
owner.Attack(this->i_target.getTarget(),true); owner.Attack(this->i_target.getTarget(),true);
} }

View file

@ -330,7 +330,7 @@ HostileReference* ThreatContainer::selectNextVictim(Creature* pAttacker, Hostile
if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
(currentRef->getThreat() > 1.1f * 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 { //implement 110% threat rule for targets in melee range
found = true; //and 130% rule for targets in ranged distances found = true; //and 130% rule for targets in ranged distances
break; //for selecting alive targets break; //for selecting alive targets

View file

@ -445,13 +445,23 @@ void Unit::resetAttackTimer(WeaponAttackType type)
m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[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); MANGOS_ASSERT(pVictim);
float reach = GetFloatValue(UNIT_FIELD_COMBATREACH);
if( reach <= 0.0f ) // The measured values show BASE_MELEE_OFFSET in (1.3224, 1.342)
reach = 1.0f; float reach = GetFloatValue(UNIT_FIELD_COMBATREACH) + pVictim->GetFloatValue(UNIT_FIELD_COMBATREACH) +
return IsWithinDistInMap(pVictim, reach); 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) void Unit::RemoveSpellsCausingAura(AuraType auraType)

View file

@ -125,6 +125,7 @@ enum SpellFacingFlags
SPELL_FACING_FLAG_INFRONT = 0x0001 SPELL_FACING_FLAG_INFRONT = 0x0001
}; };
#define BASE_MELEERANGE_OFFSET 1.33f
#define BASE_MINDAMAGE 1.0f #define BASE_MINDAMAGE 1.0f
#define BASE_MAXDAMAGE 2.0f #define BASE_MAXDAMAGE 2.0f
#define BASE_ATTACK_TIME 2000 #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); 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; uint32 m_extraAttacks;
void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*) void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*)

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__ #ifndef __REVISION_NR_H__
#define __REVISION_NR_H__ #define __REVISION_NR_H__
#define REVISION_NR "11104" #define REVISION_NR "11105"
#endif // __REVISION_NR_H__ #endif // __REVISION_NR_H__