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:
@@ -127,6 +127,26 @@ void CreatureAI::MoveInLineOfSight(Unit* who)
|
|||||||
AttackStart(who);
|
AttackStart(who);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Distract creature, if player gets too close while stealthed/prowling
|
||||||
|
void CreatureAI::TriggerAlert(Unit const* who) const
|
||||||
|
{
|
||||||
|
// If there's no target, or target isn't a player do nothing
|
||||||
|
if (!who || who->GetTypeId() != TYPEID_PLAYER)
|
||||||
|
return;
|
||||||
|
// If this unit isn't an NPC, is already distracted, is in combat, is confused, stunned or fleeing, do nothing
|
||||||
|
if (me->GetTypeId() != TYPEID_UNIT || me->IsInCombat() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED))
|
||||||
|
return;
|
||||||
|
// Only alert for hostiles!
|
||||||
|
if (me->IsCivilian() || me->HasReactState(REACT_PASSIVE) || !me->IsHostileTo(who) || !me->_IsTargetAcceptable(who))
|
||||||
|
return;
|
||||||
|
// Send alert sound (if any) for this creature
|
||||||
|
me->SendAIReaction(AI_REACTION_ALERT);
|
||||||
|
// Face the unit (stealthed player) and set distracted state for 5 seconds
|
||||||
|
me->SetFacingTo(me->GetAngle(who->GetPositionX(), who->GetPositionY()));
|
||||||
|
me->StopMoving();
|
||||||
|
me->GetMotionMaster()->MoveDistract(5 * IN_MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
void CreatureAI::EnterEvadeMode()
|
void CreatureAI::EnterEvadeMode()
|
||||||
{
|
{
|
||||||
if (!_EnterEvadeMode())
|
if (!_EnterEvadeMode())
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ class CreatureAI : public UnitAI
|
|||||||
// Called if IsVisible(Unit* who) is true at each who move, reaction at visibility zone enter
|
// Called if IsVisible(Unit* who) is true at each who move, reaction at visibility zone enter
|
||||||
void MoveInLineOfSight_Safe(Unit* who);
|
void MoveInLineOfSight_Safe(Unit* who);
|
||||||
|
|
||||||
|
// Trigger Creature "Alert" state (creature can see stealthed unit)
|
||||||
|
void TriggerAlert(Unit const* who) const;
|
||||||
|
|
||||||
// Called in Creature::Update when deathstate = DEAD. Inherited classes may maniuplate the ability to respawn based on scripted events.
|
// Called in Creature::Update when deathstate = DEAD. Inherited classes may maniuplate the ability to respawn based on scripted events.
|
||||||
virtual bool CanRespawn() { return true; }
|
virtual bool CanRespawn() { return true; }
|
||||||
|
|
||||||
|
|||||||
@@ -96,9 +96,17 @@ void npc_escortAI::MoveInLineOfSight(Unit* who)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (me->CanStartAttack(who))
|
if (me->CanStartAttack(who))
|
||||||
|
{
|
||||||
|
if (me->HasUnitState(UNIT_STATE_DISTRACTED))
|
||||||
|
{
|
||||||
|
me->ClearUnitState(UNIT_STATE_DISTRACTED);
|
||||||
|
me->GetMotionMaster()->Clear();
|
||||||
|
}
|
||||||
AttackStart(who);
|
AttackStart(who);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void npc_escortAI::JustDied(Unit* /*killer*/)
|
void npc_escortAI::JustDied(Unit* /*killer*/)
|
||||||
{
|
{
|
||||||
if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort)
|
if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort)
|
||||||
|
|||||||
@@ -89,8 +89,15 @@ void FollowerAI::MoveInLineOfSight(Unit* who)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (me->CanStartAttack(who))
|
if (me->CanStartAttack(who))
|
||||||
|
{
|
||||||
|
if (me->HasUnitState(UNIT_STATE_DISTRACTED))
|
||||||
|
{
|
||||||
|
me->ClearUnitState(UNIT_STATE_DISTRACTED);
|
||||||
|
me->GetMotionMaster()->Clear();
|
||||||
|
}
|
||||||
AttackStart(who);
|
AttackStart(who);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FollowerAI::JustDied(Unit* /*pKiller*/)
|
void FollowerAI::JustDied(Unit* /*pKiller*/)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -674,8 +674,15 @@ void SmartAI::MoveInLineOfSight(Unit* who)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (me->CanStartAttack(who))
|
if (me->CanStartAttack(who))
|
||||||
|
{
|
||||||
|
if (me->HasUnitState(UNIT_STATE_DISTRACTED))
|
||||||
|
{
|
||||||
|
me->ClearUnitState(UNIT_STATE_DISTRACTED);
|
||||||
|
me->GetMotionMaster()->Clear();
|
||||||
|
}
|
||||||
AttackStart(who);
|
AttackStart(who);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool SmartAI::CanAIAttack(const Unit* /*who*/) const
|
bool SmartAI::CanAIAttack(const Unit* /*who*/) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2809,3 +2809,45 @@ void Creature::ReleaseFocus(Spell const* focusSpell)
|
|||||||
if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
|
if (focusSpell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_DONT_TURN_DURING_CAST))
|
||||||
ClearUnitState(UNIT_STATE_ROTATING);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -596,6 +596,7 @@ class Creature : public Unit, public GridObject<Creature>, public MovableMapObje
|
|||||||
|
|
||||||
bool CanStartAttack(Unit const* u) const;
|
bool CanStartAttack(Unit const* u) const;
|
||||||
float GetAggroRange(Unit const* target) const;
|
float GetAggroRange(Unit const* target) const;
|
||||||
|
float GetAttackDistance(Unit const* player) const;
|
||||||
|
|
||||||
void SendAIReaction(AiReaction reactionType);
|
void SendAIReaction(AiReaction reactionType);
|
||||||
|
|
||||||
|
|||||||
@@ -1551,7 +1551,7 @@ float WorldObject::GetSightRange(const WorldObject* target) const
|
|||||||
return 0.0f;
|
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)
|
if (this == obj)
|
||||||
return true;
|
return true;
|
||||||
@@ -1652,7 +1652,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
|
|||||||
if (((const Player*)this)->IsSpectator() && ((const Player*)this)->FindMap()->IsBattleArena() && (obj->m_invisibility.GetFlags() || obj->m_stealth.GetFlags()))
|
if (((const Player*)this)->IsSpectator() && ((const Player*)this)->FindMap()->IsBattleArena() && (obj->m_invisibility.GetFlags() || obj->m_stealth.GetFlags()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanDetect(obj, ignoreStealth, !distanceCheck))
|
if (!CanDetect(obj, ignoreStealth, !distanceCheck, checkAlert))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1665,7 +1665,7 @@ bool WorldObject::CanNeverSee(WorldObject const* obj) const
|
|||||||
return GetMap() != obj->GetMap() || !InSamePhase(obj);
|
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;
|
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!
|
if (!seer->CanDetectInvisibilityOf(obj)) // xinef: added ignoreStealth, allow AoE spells to hit invisible targets!
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!seer->CanDetectStealthOf(obj))
|
if (!seer->CanDetectStealthOf(obj, checkAlert))
|
||||||
{
|
{
|
||||||
// xinef: ignore units players have at client, this cant be cheated!
|
// xinef: ignore units players have at client, this cant be cheated!
|
||||||
if (checkClient)
|
if (checkClient)
|
||||||
@@ -1740,7 +1740,7 @@ bool WorldObject::CanDetectInvisibilityOf(WorldObject const* obj) const
|
|||||||
return true;
|
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),
|
// Combat reach is the minimal distance (both in front and behind),
|
||||||
// and it is also used in the range calculation.
|
// and it is also used in the range calculation.
|
||||||
@@ -1797,9 +1797,22 @@ bool WorldObject::CanDetectStealthOf(WorldObject const* obj) const
|
|||||||
// Calculate max distance
|
// Calculate max distance
|
||||||
float visibilityRange = float(detectionValue) * 0.3f + combatReach;
|
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;
|
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)
|
if (distance > visibilityRange)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -906,7 +906,8 @@ class WorldObject : public Object, public WorldLocation
|
|||||||
float GetGridActivationRange() const;
|
float GetGridActivationRange() const;
|
||||||
float GetVisibilityRange() const;
|
float GetVisibilityRange() const;
|
||||||
float GetSightRange(const WorldObject* target = NULL) 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_stealth;
|
||||||
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealthDetect;
|
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;
|
bool CanNeverSee(WorldObject const* obj) const;
|
||||||
virtual bool CanAlwaysSee(WorldObject const* /*obj*/) const { return false; }
|
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 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
|
namespace Trinity
|
||||||
|
|||||||
@@ -12648,7 +12648,8 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
|
|||||||
return false;
|
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.
|
// 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;
|
return false;
|
||||||
|
|
||||||
// can't attack dead
|
// can't attack dead
|
||||||
@@ -19449,3 +19450,22 @@ void Unit::setRace(uint8 race)
|
|||||||
if (GetTypeId() == TYPEID_PLAYER)
|
if (GetTypeId() == TYPEID_PLAYER)
|
||||||
m_race = race;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1687,6 +1687,8 @@ class Unit : public WorldObject
|
|||||||
bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); }
|
bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); }
|
||||||
|
|
||||||
bool IsInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); }
|
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); }
|
bool IsPetInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); }
|
||||||
void CombatStart(Unit* target, bool initialAggro = true);
|
void CombatStart(Unit* target, bool initialAggro = true);
|
||||||
void CombatStartOnCast(Unit* target, bool initialAggro = true, uint32 duration = 0);
|
void CombatStartOnCast(Unit* target, bool initialAggro = true, uint32 duration = 0);
|
||||||
|
|||||||
@@ -128,9 +128,18 @@ inline void CreatureUnitRelocationWorker(Creature* c, Unit* u)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (c->HasReactState(REACT_AGGRESSIVE) && !c->HasUnitState(UNIT_STATE_SIGHTLESS))
|
if (c->HasReactState(REACT_AGGRESSIVE) && !c->HasUnitState(UNIT_STATE_SIGHTLESS))
|
||||||
|
{
|
||||||
if (c->IsAIEnabled && c->CanSeeOrDetect(u, false, true))
|
if (c->IsAIEnabled && c->CanSeeOrDetect(u, false, true))
|
||||||
|
{
|
||||||
c->AI()->MoveInLineOfSight_Safe(u);
|
c->AI()->MoveInLineOfSight_Safe(u);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (u->GetTypeId() == TYPEID_PLAYER && u->HasStealthAura() && c->IsAIEnabled && c->CanSeeOrDetect(u, false, true, true))
|
||||||
|
c->AI()->TriggerAlert(u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PlayerRelocationNotifier::Visit(PlayerMapType &m)
|
void PlayerRelocationNotifier::Visit(PlayerMapType &m)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -69,12 +69,23 @@ void RotateMovementGenerator::Finalize(Unit* unit)
|
|||||||
|
|
||||||
void DistractMovementGenerator::Initialize(Unit* owner)
|
void DistractMovementGenerator::Initialize(Unit* owner)
|
||||||
{
|
{
|
||||||
|
// Distracted creatures stand up if not standing
|
||||||
|
if (!owner->IsStandState())
|
||||||
|
owner->SetStandState(UNIT_STAND_STATE_STAND);
|
||||||
|
|
||||||
owner->AddUnitState(UNIT_STATE_DISTRACTED);
|
owner->AddUnitState(UNIT_STATE_DISTRACTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DistractMovementGenerator::Finalize(Unit* owner)
|
void DistractMovementGenerator::Finalize(Unit* owner)
|
||||||
{
|
{
|
||||||
owner->ClearUnitState(UNIT_STATE_DISTRACTED);
|
owner->ClearUnitState(UNIT_STATE_DISTRACTED);
|
||||||
|
|
||||||
|
// If this is a creature, then return orientation to original position (for idle movement creatures)
|
||||||
|
if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature())
|
||||||
|
{
|
||||||
|
float angle = owner->ToCreature()->GetHomePosition().GetOrientation();
|
||||||
|
owner->SetFacingTo(angle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DistractMovementGenerator::Update(Unit* owner, uint32 time_diff)
|
bool DistractMovementGenerator::Update(Unit* owner, uint32 time_diff)
|
||||||
|
|||||||
Reference in New Issue
Block a user