diff options
-rw-r--r-- | src/server/game/AI/CreatureAI.cpp | 24 | ||||
-rw-r--r-- | src/server/game/AI/CreatureAI.h | 3 | ||||
-rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp | 8 | ||||
-rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp | 8 | ||||
-rw-r--r-- | src/server/game/AI/SmartScripts/SmartAI.cpp | 8 | ||||
-rw-r--r-- | src/server/game/Entities/Object/Object.cpp | 22 | ||||
-rw-r--r-- | src/server/game/Entities/Object/Object.h | 6 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 26 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 1 | ||||
-rw-r--r-- | src/server/game/Grids/Notifiers/GridNotifiers.cpp | 5 | ||||
-rwxr-xr-x | src/server/game/Movement/MovementGenerators/IdleMovementGenerator.cpp | 11 |
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 0b040fc519a..1a51bb2d897 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 c9c70e97513..b3b757e6a2e 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. @@ -11877,8 +11899,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) |