diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 777dd34c7d..66d3e87071 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3101,7 +3101,7 @@ void Creature::SetObjectScale(float scale) if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(GetDisplayId())) { SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (IsPet() ? 1.0f : minfo->bounding_radius) * scale); - SetFloatValue(UNIT_FIELD_COMBATREACH, (IsPet() ? DEFAULT_COMBAT_REACH : minfo->combat_reach) * scale); + SetFloatValue(UNIT_FIELD_COMBATREACH, (IsPet() ? DEFAULT_PLAYER_COMBAT_REACH : minfo->combat_reach) * scale); } } @@ -3112,7 +3112,7 @@ void Creature::SetDisplayId(uint32 modelId) if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId)) { SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, (IsPet() ? 1.0f : minfo->bounding_radius) * GetObjectScale()); - SetFloatValue(UNIT_FIELD_COMBATREACH, (IsPet() ? DEFAULT_COMBAT_REACH : minfo->combat_reach) * GetObjectScale()); + SetFloatValue(UNIT_FIELD_COMBATREACH, (IsPet() ? DEFAULT_PLAYER_COMBAT_REACH : minfo->combat_reach) * GetObjectScale()); } } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index e50573c50a..e53db7197a 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1303,29 +1303,19 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool sizefactor += incTargetRadius ? obj->GetCombatReach() : 0.0f; float maxdist = dist2compare + sizefactor; + Position const* thisOrTransport = this; + Position const* objOrObjTransport = obj; + if (GetTransport() && obj->GetTransport() && obj->GetTransport()->GetGUID().GetCounter() == GetTransport()->GetGUID().GetCounter()) { - float dtx = m_movementInfo.transport.pos.m_positionX - obj->m_movementInfo.transport.pos.m_positionX; - float dty = m_movementInfo.transport.pos.m_positionY - obj->m_movementInfo.transport.pos.m_positionY; - float disttsq = dtx * dtx + dty * dty; - if (is3D) - { - float dtz = m_movementInfo.transport.pos.m_positionZ - obj->m_movementInfo.transport.pos.m_positionZ; - disttsq += dtz * dtz; - } - return disttsq < (maxdist * maxdist); + thisOrTransport = &m_movementInfo.transport.pos; + objOrObjTransport = &obj->m_movementInfo.transport.pos; } - float dx = GetPositionX() - obj->GetPositionX(); - float dy = GetPositionY() - obj->GetPositionY(); - float distsq = dx*dx + dy*dy; if (is3D) - { - float dz = GetPositionZ() - obj->GetPositionZ(); - distsq += dz*dz; - } - - return distsq < maxdist * maxdist; + return thisOrTransport->IsInDist(objOrObjTransport, maxdist); + else + return thisOrTransport->IsInDist2d(objOrObjTransport, maxdist); } float WorldObject::GetDistance(const WorldObject* obj) const diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h index b0db49bcf9..43f2ea15a4 100644 --- a/src/server/game/Entities/Object/ObjectDefines.h +++ b/src/server/game/Entities/Object/ObjectDefines.h @@ -36,11 +36,11 @@ #define DEFAULT_VISIBILITY_INSTANCE 170.0f // default visible distance in instances, 170 yards #define DEFAULT_VISIBILITY_BGARENAS 533.0f // default visible distance in BG/Arenas, roughly 533 yards -#define DEFAULT_WORLD_OBJECT_SIZE 0.388999998569489f // player size, also currently used (correctly?) for any non Unit world objects -#define DEFAULT_COMBAT_REACH 1.5f -#define MIN_MELEE_REACH 2.0f -#define NOMINAL_MELEE_RANGE 5.0f -#define MELEE_RANGE (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players +#define DEFAULT_PLAYER_BOUNDING_RADIUS 0.388999998569489f // player size, also currently used (correctly?) for any non Unit world objects +#define DEFAULT_PLAYER_COMBAT_REACH 1.5f +#define MIN_MELEE_REACH 2.0f +#define NOMINAL_MELEE_RANGE 5.0f +#define MELEE_RANGE (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players enum class VisibilityDistanceType : uint8 { diff --git a/src/server/game/Entities/Object/Position.cpp b/src/server/game/Entities/Object/Position.cpp index e55a35c1a2..bda78f7abf 100644 --- a/src/server/game/Entities/Object/Position.cpp +++ b/src/server/game/Entities/Object/Position.cpp @@ -148,6 +148,12 @@ bool Position::IsWithinBox(const Position& center, float xradius, float yradius, return true; } +bool Position::IsWithinDoubleVerticalCylinder(Position const* center, float radius, float height) const +{ + float verticalDelta = GetPositionZ() - center->GetPositionZ(); + return IsInDist2d(center, radius) && std::abs(verticalDelta) <= height; +} + bool Position::HasInArc(float arc, const Position* obj, float border) const { // always have self in arc diff --git a/src/server/game/Entities/Object/Position.h b/src/server/game/Entities/Object/Position.h index b0200e5b2a..32334fb3f0 100644 --- a/src/server/game/Entities/Object/Position.h +++ b/src/server/game/Entities/Object/Position.h @@ -207,6 +207,11 @@ public: bool IsInDist(Position const* pos, float dist) const { return GetExactDistSq(pos) < dist * dist; } bool IsWithinBox(const Position& center, float xradius, float yradius, float zradius) const; + + /* + search using this relation: dist2d < radius && abs(dz) < height + */ + bool IsWithinDoubleVerticalCylinder(Position const* center, float radius, float height) const; bool HasInArc(float arcangle, Position const* pos, float border = 2.0f) const; bool HasInLine(Position const* pos, float objSize, float width) const; std::string ToString() const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index feec24b907..bd6d854686 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1799,8 +1799,8 @@ void Player::RemoveFromWorld() void Player::SetObjectScale(float scale) { Unit::SetObjectScale(scale); - SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, scale * DEFAULT_WORLD_OBJECT_SIZE); - SetFloatValue(UNIT_FIELD_COMBATREACH, scale * DEFAULT_COMBAT_REACH); + SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, scale * DEFAULT_PLAYER_BOUNDING_RADIUS); + SetFloatValue(UNIT_FIELD_COMBATREACH, scale * DEFAULT_PLAYER_COMBAT_REACH); if (IsInWorld()) SendMovementSetCollisionHeight(GetCollisionHeight(), UPDATE_COLLISION_HEIGHT_SCALE); } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 0a624d8b45..aa2faebf97 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -945,30 +945,46 @@ namespace Trinity class AnyFriendlyUnitInObjectRangeCheck { public: - AnyFriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool playerOnly = false, bool noPlayerTarget = false) : i_obj(obj), i_funit(funit), i_range(range), i_playerOnly(playerOnly), i_noPlayerTarget(noPlayerTarget) { } + AnyFriendlyUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true, bool noPlayerTarget = false) + : i_obj(obj), i_funit(funit), i_range(range), i_playerOnly(playerOnly), i_incOwnRadius(incOwnRadius), i_incTargetRadius(incTargetRadius), i_noPlayerTarget(noPlayerTarget) { } + bool operator()(Unit* u) { - if (u->IsAlive() - && i_obj->IsWithinDistInMap(u, i_range) - && i_funit->IsFriendlyTo(u) - && (!i_playerOnly || u->GetTypeId() == TYPEID_PLAYER) - && (!i_noPlayerTarget || u->GetTypeId() != TYPEID_PLAYER)) - return true; - else + if (!u->IsAlive()) return false; + + float searchRadius = i_range; + if (i_incOwnRadius) + searchRadius += i_obj->GetCombatReach(); + if (i_incTargetRadius) + searchRadius += u->GetCombatReach(); + + if (!u->IsInMap(i_obj) || !u->IsInPhase(i_obj) || !u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius)) + return false; + + if (!i_funit->IsFriendlyTo(u)) + return false; + + if (i_playerOnly || u->GetTypeId() != TYPEID_PLAYER) + return false; + + return !i_noPlayerTarget || u->GetTypeId() != TYPEID_PLAYER; } private: WorldObject const* i_obj; Unit const* i_funit; float i_range; bool i_playerOnly; + bool i_incOwnRadius; + bool i_incTargetRadius; bool i_noPlayerTarget; }; class AnyGroupedUnitInObjectRangeCheck { public: - AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid, bool playerOnly = false) : _source(obj), _refUnit(funit), _range(range), _raid(raid), _playerOnly(playerOnly) { } + AnyGroupedUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, bool raid, bool playerOnly = false, bool incOwnRadius = true, bool incTargetRadius = true) + : _source(obj), _refUnit(funit), _range(range), _raid(raid), _playerOnly(playerOnly), i_incOwnRadius(incOwnRadius), i_incTargetRadius(incTargetRadius) { } bool operator()(Unit* u) { if (_playerOnly && u->GetTypeId() != TYPEID_PLAYER) @@ -982,7 +998,19 @@ namespace Trinity else if (!_refUnit->IsInPartyWith(u)) return false; - return !_refUnit->IsHostileTo(u) && u->IsAlive() && _source->IsWithinDistInMap(u, _range); + if (_refUnit->IsHostileTo(u)) + return false; + + if (!u->IsAlive()) + return false; + + float searchRadius = _range; + if (i_incOwnRadius) + searchRadius += _source->GetCombatReach(); + if (i_incTargetRadius) + searchRadius += u->GetCombatReach(); + + return u->IsInMap(_source) && u->IsInPhase(_source) && u->IsWithinDoubleVerticalCylinder(_source, searchRadius, searchRadius); } private: @@ -991,6 +1019,8 @@ namespace Trinity float _range; bool _raid; bool _playerOnly; + bool i_incOwnRadius; + bool i_incTargetRadius; }; class AnyUnitInObjectRangeCheck @@ -1037,8 +1067,8 @@ namespace Trinity class AnyAoETargetUnitInObjectRangeCheck { public: - AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, SpellInfo const* spellInfo = nullptr) - : i_obj(obj), i_funit(funit), _spellInfo(spellInfo), i_range(range) + AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, SpellInfo const* spellInfo = nullptr, bool incOwnRadius = true, bool incTargetRadius = true) + : i_obj(obj), i_funit(funit), _spellInfo(spellInfo), i_range(range), i_incOwnRadius(incOwnRadius), i_incTargetRadius(incTargetRadius) { Unit const* check = i_funit; Unit const* owner = i_funit->GetOwner(); @@ -1062,7 +1092,16 @@ namespace Trinity if (_spellInfo && _spellInfo->HasAttribute(SPELL_ATTR5_DONT_TARGET_PLAYERS) && u->GetTypeId() == TYPEID_PLAYER) return false; - return i_funit->_IsValidAttackTarget(u, _spellInfo, i_obj->GetTypeId() == TYPEID_DYNAMICOBJECT ? i_obj : nullptr) && i_obj->IsWithinDistInMap(u, i_range); + if (!i_funit->_IsValidAttackTarget(u, _spellInfo, i_obj->GetTypeId() == TYPEID_DYNAMICOBJECT ? i_obj : nullptr)) + return false; + + float searchRadius = i_range; + if (i_incOwnRadius) + searchRadius += i_obj->GetCombatReach(); + if (i_incTargetRadius) + searchRadius += u->GetCombatReach(); + + return u->IsInMap(i_obj) && u->IsInPhase(i_obj) && u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius); } private: bool i_targetForPlayer; @@ -1070,6 +1109,8 @@ namespace Trinity Unit const* i_funit; SpellInfo const* _spellInfo; float i_range; + bool i_incOwnRadius; + bool i_incTargetRadius; }; // do attack at call of help to friendly crearture diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 822d22b8bd..de0655b7e3 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1364,6 +1364,11 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge radius = m_spellInfo->GetMaxRange(m_spellInfo->IsPositiveEffect(effIndex), m_caster, this); radius *= m_spellValue->RadiusMod; + + // if this is a proximity based aoe (Frost Nova, Psychic Scream, ...), include the caster's own combat reach + if (targetType.IsProximityBasedAoe()) + radius += GetCaster()->GetCombatReach(); + std::list targets; switch (targetType.GetTarget()) { @@ -1448,7 +1453,7 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici float dist = frand(minDist, maxDist); float x, y, z; float angle = float(rand_norm()) * static_cast(M_PI * 35.0f / 180.0f) - static_cast(M_PI * 17.5f / 180.0f); - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE, dist, angle); + m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS, dist, angle); float ground = m_caster->GetMapHeight(x, y, z); float liquidLevel = VMAP_INVALID_HEIGHT_VALUE; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index aa0e04f9b3..9682734a47 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2940,7 +2940,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex) destTarget->GetPosition(x, y, z, o); else { - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS); o = target->GetOrientation(); } @@ -3999,7 +3999,7 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) // Summon in random point all other units if location present else { - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS); o = m_caster->GetOrientation(); } @@ -4780,7 +4780,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) else if (m_spellInfo->Effects[effIndex].HasRadius() && m_spellInfo->Speed == 0) { float dis = m_spellInfo->Effects[effIndex].CalcRadius(m_originalCaster); - m_caster->GetClosePoint(fx, fy, fz, DEFAULT_WORLD_OBJECT_SIZE, dis); + m_caster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); fo = m_caster->GetOrientation(); } else @@ -4790,7 +4790,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) float max_dis = m_spellInfo->GetMaxRange(true); float dis = (float)rand_norm() * (max_dis - min_dis) + min_dis; - m_caster->GetClosePoint(fx, fy, fz, DEFAULT_WORLD_OBJECT_SIZE, dis); + m_caster->GetClosePoint(fx, fy, fz, DEFAULT_PLAYER_BOUNDING_RADIUS, dis); fo = m_caster->GetOrientation(); } @@ -5580,7 +5580,7 @@ void Spell::EffectSummonPersonalGameObject(SpellEffIndex effIndex) destTarget->GetPosition(x, y, z, o); else { - m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + m_caster->GetClosePoint(x, y, z, DEFAULT_PLAYER_BOUNDING_RADIUS); o = m_caster->GetOrientation(); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index fbf7fe2b83..52453c1368 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -74,6 +74,35 @@ bool SpellImplicitTargetInfo::IsArea() const return GetSelectionCategory() == TARGET_SELECT_CATEGORY_AREA || GetSelectionCategory() == TARGET_SELECT_CATEGORY_CONE; } +bool SpellImplicitTargetInfo::IsProximityBasedAoe() const +{ + switch (_target) + { + case TARGET_UNIT_SRC_AREA_ENTRY: + case TARGET_UNIT_SRC_AREA_ENEMY: + case TARGET_UNIT_CASTER_AREA_PARTY: + case TARGET_UNIT_SRC_AREA_ALLY: + case TARGET_UNIT_SRC_AREA_PARTY: + case TARGET_UNIT_LASTTARGET_AREA_PARTY: + case TARGET_GAMEOBJECT_SRC_AREA: + case TARGET_UNIT_CASTER_AREA_RAID: + case TARGET_CORPSE_SRC_AREA_ENEMY: + return true; + + case TARGET_UNIT_DEST_AREA_ENTRY: + case TARGET_UNIT_DEST_AREA_ENEMY: + case TARGET_UNIT_DEST_AREA_ALLY: + case TARGET_UNIT_DEST_AREA_PARTY: + case TARGET_GAMEOBJECT_DEST_AREA: + case TARGET_UNIT_TARGET_AREA_RAID_CLASS: + return false; + + default: + TC_LOG_WARN("spells", "SpellImplicitTargetInfo::IsProximityBasedAoe called a non-aoe spell"); + return false; + } +} + SpellTargetSelectionCategories SpellImplicitTargetInfo::GetSelectionCategory() const { return _data[_target].SelectionCategory; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 1d74ca77b9..c54106c4c0 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -218,6 +218,7 @@ public: SpellImplicitTargetInfo(uint32 target); bool IsArea() const; + bool IsProximityBasedAoe() const; SpellTargetSelectionCategories GetSelectionCategory() const; SpellTargetReferenceTypes GetReferenceType() const; SpellTargetObjectTypes GetObjectType() const;