aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpete318 <pete318@hotmail.com>2015-06-28 23:42:47 +0000
committerpete318 <pete318@hotmail.com>2015-07-02 23:11:20 +0000
commit1e0213bc57922719d4f7e8897cb7e8871c138fdc (patch)
tree14ae56957aa9e87eb366bdc28707ae548a2b584a
parent275bb9303438a3e6e21e90de514723fd4aba7b05 (diff)
The following stealth changes are implemented.
- Combat no longer removes stealth, only damage does - Creatures will pursue a stealthed unit they cannot see if they're already in combat with them - When a player is ~~3 yards~~ 8% + 1.5 yards away from the usual stealth detection distance, the creature will perform the "alerted" effect. - When sitting/sleeping creatures are distracted or alerted, they will stand up - Idle movement creatures will return to their original (spawn) orientation after distract/alert - When entering combat with a distracted/alerted creature, distract state is removed - NPC no longer have a limit to stealth visibility (granted by stealth modifier spells/buffs) - If alert visibility is greater than aggro range, no alert sent
-rw-r--r--src/server/game/AI/CreatureAI.cpp24
-rw-r--r--src/server/game/AI/CreatureAI.h3
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp8
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp8
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp8
-rw-r--r--src/server/game/Entities/Object/Object.cpp22
-rw-r--r--src/server/game/Entities/Object/Object.h6
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp26
-rw-r--r--src/server/game/Entities/Unit/Unit.h1
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.cpp5
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp11
11 files changed, 108 insertions, 14 deletions
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp
index 96a25588120..d76d106e81a 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -140,6 +140,30 @@ void CreatureAI::MoveInLineOfSight(Unit* who)
// me->GetMotionMaster()->MoveChase(who->GetVictim());
}
+// 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()
{
if (!_EnterEvadeMode())
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index 2e862c37c0e..33616d076e7 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -89,6 +89,9 @@ class CreatureAI : public UnitAI
// Called if IsVisible(Unit* who) is true at each who move, reaction at visibility zone enter
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.
virtual bool CanRespawn() { return true; }
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index ed87804474a..f1c53e2f9eb 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -120,7 +120,13 @@ void npc_escortAI::MoveInLineOfSight(Unit* who)
{
if (!me->GetVictim())
{
- who->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
+ // Clear distracted state on combat
+ if (me->HasUnitState(UNIT_STATE_DISTRACTED))
+ {
+ me->ClearUnitState(UNIT_STATE_DISTRACTED);
+ me->GetMotionMaster()->Clear();
+ }
+
AttackStart(who);
}
else if (me->GetMap()->IsDungeon())
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index 7fd21a8f791..8985f722cf2 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -117,7 +117,13 @@ void FollowerAI::MoveInLineOfSight(Unit* who)
{
if (!me->GetVictim())
{
- who->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
+ // Clear distracted state on combat
+ if (me->HasUnitState(UNIT_STATE_DISTRACTED))
+ {
+ me->ClearUnitState(UNIT_STATE_DISTRACTED);
+ me->GetMotionMaster()->Clear();
+ }
+
AttackStart(who);
}
else if (me->GetMap()->IsDungeon())
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 21a15fa4f99..97d2d718ff0 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -469,7 +469,13 @@ void SmartAI::MoveInLineOfSight(Unit* who)
{
if (!me->GetVictim())
{
- who->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
+ // Clear distracted state on combat
+ if (me->HasUnitState(UNIT_STATE_DISTRACTED))
+ {
+ me->ClearUnitState(UNIT_STATE_DISTRACTED);
+ me->GetMotionMaster()->Clear();
+ }
+
AttackStart(who);
}
else/* if (me->GetMap()->IsDungeon())*/
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 6392c3ee51b..fc55c09bffa 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1539,7 +1539,7 @@ 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;
@@ -1611,7 +1611,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
if (obj->IsInvisibleDueToDespawn())
return false;
- if (!CanDetect(obj, ignoreStealth))
+ if (!CanDetect(obj, ignoreStealth, checkAlert))
return false;
return true;
@@ -1622,7 +1622,7 @@ bool WorldObject::CanNeverSee(WorldObject const* obj) const
return GetMap() != obj->GetMap() || !InSamePhase(obj);
}
-bool WorldObject::CanDetect(WorldObject const* obj, bool ignoreStealth) const
+bool WorldObject::CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkAlert) const
{
const WorldObject* seer = this;
@@ -1637,7 +1637,7 @@ bool WorldObject::CanDetect(WorldObject const* obj, bool ignoreStealth) const
if (!ignoreStealth && !seer->CanDetectInvisibilityOf(obj))
return false;
- if (!ignoreStealth && !seer->CanDetectStealthOf(obj))
+ if (!ignoreStealth && !seer->CanDetectStealthOf(obj, checkAlert))
return false;
return true;
@@ -1674,7 +1674,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.
@@ -1724,9 +1724,19 @@ bool WorldObject::CanDetectStealthOf(WorldObject const* obj) const
// Calculate max distance
float visibilityRange = float(detectionValue) * 0.3f + combatReach;
- if (visibilityRange > MAX_PLAYER_STEALTH_DETECT_RANGE)
+ // 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;
+ // When checking for alert state, look 8% further, and then 1.5 yards more than that.
+ if (checkAlert)
+ visibilityRange += (visibilityRange * 0.08f) + 1.5f;
+
+ // If checking for alert, and creature's visibility range is greater than aggro distance, No alert
+ Unit const* tunit = obj->ToUnit();
+ if (checkAlert && unit && unit->ToCreature() && visibilityRange >= unit->ToCreature()->GetAttackDistance(tunit) + unit->ToCreature()->m_CombatDistance)
+ return false;
+
if (distance > visibilityRange)
return false;
}
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index a560afa7f1b..e673e346a5f 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -514,7 +514,7 @@ class WorldObject : public Object, public WorldLocation
float GetGridActivationRange() const;
float GetVisibilityRange() const;
float GetSightRange(WorldObject const* 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, bool checkAlert = false) const;
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealth;
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealthDetect;
@@ -634,9 +634,9 @@ 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) const;
+ bool CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkAlert = false) const;
bool CanDetectInvisibilityOf(WorldObject const* obj) const;
- bool CanDetectStealthOf(WorldObject const* obj) const;
+ bool CanDetectStealthOf(WorldObject const* obj, bool checkAlert = false) const;
};
namespace Trinity
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 17915e53cfb..f551df6c260 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -288,6 +288,28 @@ Unit::~Unit()
ASSERT(m_dynObj.empty());
}
+// 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
+ ObjectGuid 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;
+}
+
void Unit::Update(uint32 p_time)
{
// WARNING! Order of execution here is important, do not change.
@@ -11885,8 +11907,8 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
if (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))
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()) : !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())))
+ // 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. 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
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index a61b406cbbd..39b1c34be55 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1480,6 +1480,7 @@ 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;
void CombatStart(Unit* target, bool initialAggro = true);
void SetInCombatState(bool PvP, Unit* enemy = NULL);
void SetInCombatWith(Unit* enemy);
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp
index d52e559c408..15f3c7811cd 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp
@@ -132,8 +132,13 @@ inline void CreatureUnitRelocationWorker(Creature* c, Unit* u)
return;
if (c->HasReactState(REACT_AGGRESSIVE) && !c->HasUnitState(UNIT_STATE_SIGHTLESS))
+ {
if (c->IsAIEnabled && c->CanSeeOrDetect(u, false, true))
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)
diff --git a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
index 78d86fb7abb..2aaaa719e94 100755
--- a/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp
@@ -81,12 +81,23 @@ void RotateMovementGenerator::Finalize(Unit* unit)
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);
}
void DistractMovementGenerator::Finalize(Unit* owner)
{
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)