mirror of
https://github.com/araxiaonline/TrinityCore.git
synced 2026-06-15 04:32:35 -04:00
Merge pull request #14974 from pete318/stealth_work
Stealth changes (Player stealthed vs NPCs) - 3.3.5
(cherry picked from commit 90fcfb3f2d)
Conflicts:
src/server/game/Entities/Object/Object.h
src/server/game/Entities/Unit/Unit.cpp
This commit is contained in:
@@ -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())
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -473,7 +473,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())*/
|
||||
|
||||
@@ -1944,7 +1944,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;
|
||||
@@ -2016,7 +2016,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;
|
||||
@@ -2027,7 +2027,7 @@ bool WorldObject::CanNeverSee(WorldObject const* obj) const
|
||||
return GetMap() != obj->GetMap() || !IsInPhase(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;
|
||||
|
||||
@@ -2042,7 +2042,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;
|
||||
@@ -2072,7 +2072,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.
|
||||
@@ -2122,9 +2122,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;
|
||||
}
|
||||
|
||||
@@ -560,7 +560,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;
|
||||
@@ -693,9 +693,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;
|
||||
|
||||
uint16 m_aiAnimKitId;
|
||||
uint16 m_movementAnimKitId;
|
||||
|
||||
@@ -318,6 +318,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.
|
||||
@@ -9971,8 +9993,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(GetMap()->GetDifficultyID())) : !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea(GetMap()->GetDifficultyID()))))
|
||||
// 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(GetMap()->GetDifficultyID())) : !CanSeeOrDetect(target, (bySpell && bySpell->IsAffectingArea(GetMap()->GetDifficultyID())) || (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && target->IsInCombat() && IsInCombatWith(target)))))
|
||||
return false;
|
||||
|
||||
// can't attack dead
|
||||
|
||||
@@ -1557,6 +1557,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);
|
||||
|
||||
@@ -135,8 +135,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)
|
||||
|
||||
@@ -74,12 +74,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)
|
||||
|
||||
Reference in New Issue
Block a user