Core/NPC: Creatures now alert when they detect stealthed players (#1060)

Creatures stop when they detect a stealthed player like in retail.
Closes #1020
This commit is contained in:
Jinyang li
2018-11-01 03:25:23 +08:00
committed by Barbz
parent 59f0d086a6
commit 4b6342b42f
13 changed files with 166 additions and 20 deletions

View File

@@ -2809,3 +2809,45 @@ void Creature::ReleaseFocus(Spell const* focusSpell)
if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
ClearUnitState(UNIT_STATE_ROTATING);
}
float Creature::GetAttackDistance(Unit const* player) const
{
float aggroRate = sWorld->getRate(RATE_CREATURE_AGGRO);
if (aggroRate == 0)
return 0.0f;
if (!player)
return 0.0f;
uint32 playerLevel = player->getLevelForTarget(this);
uint32 creatureLevel = getLevelForTarget(player);
int32 levelDiff = static_cast<int32>(playerLevel) - static_cast<int32>(creatureLevel);
// "The maximum Aggro Radius has a cap of 25 levels under. Example: A level 30 char has the same Aggro Radius of a level 5 char on a level 60 mob."
if (levelDiff < -25)
levelDiff = -25;
// "The aggro radius of a mob having the same level as the player is roughly 20 yards"
float retDistance = 20.0f;
// "Aggro Radius varies with level difference at a rate of roughly 1 yard/level"
// radius grow if playlevel < creaturelevel
retDistance -= static_cast<float>(levelDiff);
if (creatureLevel + 5 <= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
// detect range auras
retDistance += static_cast<float>( GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE) );
// detected range auras
retDistance += static_cast<float>( player->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE) );
}
// "Minimum Aggro Radius for a mob seems to be combat range (5 yards)"
if (retDistance < 5.0f)
retDistance = 5.0f;
return (retDistance*aggroRate);
}

View File

@@ -596,6 +596,7 @@ class Creature : public Unit, public GridObject<Creature>, public MovableMapObje
bool CanStartAttack(Unit const* u) const;
float GetAggroRange(Unit const* target) const;
float GetAttackDistance(Unit const* player) const;
void SendAIReaction(AiReaction reactionType);

View File

@@ -1551,8 +1551,8 @@ float WorldObject::GetSightRange(const WorldObject* target) const
return 0.0f;
}
bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, bool distanceCheck) const
{
bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, bool distanceCheck, bool checkAlert) const
{
if (this == obj)
return true;
@@ -1647,12 +1647,12 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
if (obj->IsInvisibleDueToDespawn())
return false;
// pussywizard: arena spectator
// pussywizard: arena spectator
if (this->GetTypeId() == TYPEID_PLAYER)
if (((const Player*)this)->IsSpectator() && ((const Player*)this)->FindMap()->IsBattleArena() && (obj->m_invisibility.GetFlags() || obj->m_stealth.GetFlags()))
return false;
if (!CanDetect(obj, ignoreStealth, !distanceCheck))
if (!CanDetect(obj, ignoreStealth, !distanceCheck, checkAlert))
return false;
return true;
@@ -1665,7 +1665,7 @@ bool WorldObject::CanNeverSee(WorldObject const* obj) const
return GetMap() != obj->GetMap() || !InSamePhase(obj);
}
bool WorldObject::CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkClient) const
bool WorldObject::CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkClient, bool checkAlert) const
{
const WorldObject* seer = this;
@@ -1682,7 +1682,7 @@ bool WorldObject::CanDetect(WorldObject const* obj, bool ignoreStealth, bool che
if (!seer->CanDetectInvisibilityOf(obj)) // xinef: added ignoreStealth, allow AoE spells to hit invisible targets!
return false;
if (!seer->CanDetectStealthOf(obj))
if (!seer->CanDetectStealthOf(obj, checkAlert))
{
// xinef: ignore units players have at client, this cant be cheated!
if (checkClient)
@@ -1740,7 +1740,7 @@ bool WorldObject::CanDetectInvisibilityOf(WorldObject const* obj) const
return true;
}
bool WorldObject::CanDetectStealthOf(WorldObject const* obj) const
bool WorldObject::CanDetectStealthOf(WorldObject const* obj, bool checkAlert) const
{
// Combat reach is the minimal distance (both in front and behind),
// and it is also used in the range calculation.
@@ -1797,9 +1797,22 @@ bool WorldObject::CanDetectStealthOf(WorldObject const* obj) const
// Calculate max distance
float visibilityRange = float(detectionValue) * 0.3f + combatReach;
if (visibilityRange > MAX_PLAYER_STEALTH_DETECT_RANGE)
Unit const* unit = ToUnit();
// If this unit is an NPC then player detect range doesn't apply
if (unit && unit->GetTypeId() == TYPEID_PLAYER && visibilityRange > MAX_PLAYER_STEALTH_DETECT_RANGE)
visibilityRange = MAX_PLAYER_STEALTH_DETECT_RANGE;
if (checkAlert)
visibilityRange += (visibilityRange * 0.08f) + 1.5f;
Unit const* targetUnit = obj->ToUnit();
// If checking for alert, and creature's visibility range is greater than aggro distance, No alert
if (checkAlert && unit && unit->ToCreature() && visibilityRange >= unit->ToCreature()->GetAttackDistance(targetUnit) + unit->ToCreature()->m_CombatDistance)
return false;
if (distance > visibilityRange)
return false;
}

View File

@@ -906,7 +906,8 @@ class WorldObject : public Object, public WorldLocation
float GetGridActivationRange() const;
float GetVisibilityRange() const;
float GetSightRange(const WorldObject* target = NULL) const;
bool CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false, bool distanceCheck = false) const;
//bool CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false, bool distanceCheck = false) const;
bool CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth = false, bool distanceCheck = false, bool checkAlert = false) const;
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealth;
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealthDetect;
@@ -1047,9 +1048,11 @@ class WorldObject : public Object, public WorldLocation
bool CanNeverSee(WorldObject const* obj) const;
virtual bool CanAlwaysSee(WorldObject const* /*obj*/) const { return false; }
bool CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkClient) const;
//bool CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkClient) const;
bool CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkClient, bool checkAlert = false) const;
bool CanDetectInvisibilityOf(WorldObject const* obj) const;
bool CanDetectStealthOf(WorldObject const* obj) const;
//bool CanDetectStealthOf(WorldObject const* obj) const;
bool CanDetectStealthOf(WorldObject const* obj, bool checkAlert = false) const;
};
namespace Trinity

View File

@@ -12648,7 +12648,8 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
return false;
// can't attack invisible (ignore stealth for aoe spells) also if the area being looked at is from a spell use the dynamic object created instead of the casting unit.
if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea() && !bySpell->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)) : !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea() && !bySpell->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO))))
//Ignore stealth if target is player and unit in combat with same player
if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !CanSeeOrDetect(target, (bySpell && bySpell->IsAffectingArea()) || (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && target->IsInCombat() && IsInCombatWith(target)))))
return false;
// can't attack dead
@@ -19449,3 +19450,22 @@ void Unit::setRace(uint8 race)
if (GetTypeId() == TYPEID_PLAYER)
m_race = race;
}
// Check if unit in combat with specific unit
bool Unit::IsInCombatWith(Unit const* who) const
{
// Check target exists
if (!who)
return false;
// Search in threat list
uint64 guid = who->GetGUID();
for (ThreatContainer::StorageType::const_iterator i = m_ThreatManager.getThreatList().begin(); i != m_ThreatManager.getThreatList().end(); ++i)
{
HostileReference* ref = (*i);
// Return true if the unit matches
if (ref && ref->getUnitGuid() == guid)
return true;
}
// Nothing found, false.
return false;
}

View File

@@ -1687,6 +1687,8 @@ class Unit : public WorldObject
bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); }
bool IsInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); }
bool IsInCombatWith(Unit const* who) const;
bool IsPetInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); }
void CombatStart(Unit* target, bool initialAggro = true);
void CombatStartOnCast(Unit* target, bool initialAggro = true, uint32 duration = 0);