aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorxinef1 <w.szyszko2@gmail.com>2017-02-17 21:33:18 +0100
committerShauren <shauren.trinity@gmail.com>2019-08-17 20:04:14 +0200
commita32d5cfa1762ae1158ee2f40b4c9b36e5b41913a (patch)
treed55a3961fc9520ae7c9fa619911ea8a892559617 /src
parent2caec4f4d20b4c0f91abbcc60b756e00838c7bdd (diff)
Core/SmartAI: Various fixes and extensions for smart scripts: (#18673)
- Possible crashes fixed - Memory leak fixed - Implemented checking of vehicle conditions - Extended eventphasemask to 12 bits (sql required to change DB field type) - SMART_EVENT_GOSSIP_HELLO - added possibility to detect for gameobject reportUse call - Renamed action SMART_ACTION_SET_FLY to SMART_ACTION_SET_DISABLE_GRAVITY (to reflect actual functionality) - Added targetsLimit to action SMART_ACTION_CAST and SMART_ACTION_INVOKER_CAST to limit max amount of targets (selected randomly) - Action SMART_ACTION_TALK corrected to always work as intended - Properly call GroupEventHappens in action SMART_ACTION_CALL_GROUPEVENTHAPPENS if invoker was charmed or owned by the player - Properly utilize followAngle in action SMART_ACTION_FOLLOW (db orientation should be in degrees), but keep backward compatibility - Added action SMART_ACTION_SET_CAN_FLY (119) 0/1 - Added action SMART_ACTION_REMOVE_AURAS_BY_TYPE (120) AuraType, can be used to exit vehicle for example - Added action SMART_ACTION_SET_SIGHT_DIST (121) sightDist - Added action SMART_ACTION_FLEE (122) fleeTime - Added action SMART_ACTION_ADD_THREAT (123) +threat, -threat - Added action SMART_ACTION_LOAD_EQUIPMENT (124) equipmentId - Added action SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT (125) minId, maxId - Added action SMART_ACTION_REMOVE_ALL_GAMEOBJECTS (126), removes all owned gameobjects - Added action SMART_ACTION_STOP_MOTION (127), stopMoving, movementExpired - Extended target SMART_TARGET_HOSTILE_SECOND_AGGRO with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_HOSTILE_LAST_AGGRO with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_HOSTILE_RANDOM with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_HOSTILE_RANDOM_NOT_TOP with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_THREAT_LIST with maxdist - Extended target SMART_TARGET_OWNER_OR_SUMMONER to be able to get charmer/owner of current owner - Added new target SMART_TARGET_FARTHEST with maxDist, playerOnly, isInLos restrictions - Added SpellHit hook to GameObjectAI and extended SmartGameObjectAI to call SMART_EVENT_SPELLHIT when gameobject is hit by spell - Call GameObjectAI Reset hook on gameobject respawn (for ex. to reset one time events in smart scripts) - Fixed some logic errors in code - SmartAI Escorts properly despawn escortee if no players are in range - Disable Evading while charming creature with SmartAI - Don't call SMART_EVENT_RESPAWN for dead units before they actually respawn - Don't call SMART_EVENT_RESPAWN for not spawned gameobjects - Properly call SMART_EVENT_RESPAWN for gameobject respawn - Allow action SMART_ACTION_SET_IN_COMBAT_WITH_ZONE to utilize targetlist - Allow action SMART_ACTION_CALL_FOR_HELP to utilize targetList - Allow action SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL to utilize targetList - Allow action SMART_ACTION_SET_VISIBILITY to utilize targetList - Allow action SMART_ACTION_SET_ACTIVE to utilize targetList - Allow action SMART_ACTION_ATTACK_START to select random attack target instead of first on the list - Allow gameobjects to summon gameobjects with action SMART_ACTION_SUMMON_GO - Properly store action invokers for action SMART_ACTION_WP_START, if no player invokers are found, distance despawn check won't be used - Allow action SMART_ACTION_WP_RESUME to compensate for the state the unit actually is in (eg. combat) - Allow action SMART_ACTION_MOVE_TO_POS to select random of the avaiable targets, not only the first one. - Allow action SMART_ACTION_MOVE_TO_POS to utilize x, y, z parameters as an offset to calculated coordinates - Action SMART_ACTION_RESPAWN_TARGET should never modify respawntime of already spawned gameobjects, use dedicated function - Properly delete ontime events created by SMART_ACTION_CREATE_TIMED_EVENT - If action could not be started because conditions were not satisfied, do not recalculate the waittime to action repeattime, use smaller value to recheck more frequently - Allow target SMART_TARGET_CLOSEST_PLAYER to be used by gameobjects - Allow target SMART_TARGET_OWNER_OR_SUMMONER to be used by gameobjects - Fixed SMART_EVENT_COUNTER_SET to be only called for the id that was incremented - Changed the way counters work - Protect PhaseInc from surpassing maximum phase - Added loading checks for missing NON_REPEATABLE flag if no repeatmin, repeatmax is set - Added spell validation for SMART_ACTION_CROSS_CAST (cherrypicked from b0ae5fadd19fd172ec5154cde4f4fd14aa20ff88)
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/CoreAI/CombatAI.cpp25
-rw-r--r--src/server/game/AI/CoreAI/GameObjectAI.h3
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp37
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h27
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp182
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h21
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp472
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h31
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.cpp87
-rw-r--r--src/server/game/AI/SmartScripts/SmartScriptMgr.h109
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp11
-rw-r--r--src/server/game/Entities/Creature/Creature.h1
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp6
-rw-r--r--src/server/game/Entities/Object/Object.cpp21
-rw-r--r--src/server/game/Entities/Object/Object.h1
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp2
-rw-r--r--src/server/game/Spells/Spell.cpp4
-rw-r--r--src/server/game/Spells/SpellEffects.cpp2
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp5
-rw-r--r--src/server/scripts/World/go_scripts.cpp5
21 files changed, 813 insertions, 243 deletions
diff --git a/src/server/game/AI/CoreAI/CombatAI.cpp b/src/server/game/AI/CoreAI/CombatAI.cpp
index c565bb5baa7..54dea543516 100644
--- a/src/server/game/AI/CoreAI/CombatAI.cpp
+++ b/src/server/game/AI/CoreAI/CombatAI.cpp
@@ -307,24 +307,27 @@ void VehicleAI::LoadConditions()
void VehicleAI::CheckConditions(uint32 diff)
{
- if (m_ConditionsTimer < diff)
+ if (!m_HasConditions)
+ return;
+
+ if (m_ConditionsTimer <= diff)
{
- if (m_HasConditions)
+ if (Vehicle * vehicleKit = me->GetVehicleKit())
{
- if (Vehicle* vehicleKit = me->GetVehicleKit())
- for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
- if (Unit* passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
+ for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
+ if (Unit * passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
+ {
+ if (Player * player = passenger->ToPlayer())
{
- if (Player* player = passenger->ToPlayer())
+ if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
{
- if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
- {
- player->ExitVehicle();
- return; // check other pessanger in next tick
- }
+ player->ExitVehicle();
+ return; // check other pessanger in next tick
}
}
+ }
}
+
m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
}
else
diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h
index b188d742392..fb982ce878d 100644
--- a/src/server/game/AI/CoreAI/GameObjectAI.h
+++ b/src/server/game/AI/CoreAI/GameObjectAI.h
@@ -47,7 +47,7 @@ class TC_GAME_API GameObjectAI
static int Permissible(GameObject const* go);
- virtual bool GossipHello(Player* /*player*/, bool /*isUse*/) { return false; }
+ virtual bool GossipHello(Player* /*player*/, bool /*reportUse*/) { return false; }
virtual bool GossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { return false; }
virtual bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) { return false; }
virtual bool QuestAccept(Player* /*player*/, Quest const* /*quest*/) { return false; }
@@ -61,6 +61,7 @@ class TC_GAME_API GameObjectAI
virtual void OnGameEvent(bool /*start*/, uint16 /*eventId*/) { }
virtual void OnStateChanged(uint32 /*state*/, Unit* /*unit*/) { }
virtual void EventInform(uint32 /*eventId*/) { }
+ virtual void SpellHit(Unit* /*unit*/, const SpellInfo* /*spellInfo*/) { }
};
class TC_GAME_API NullGameObjectAI : public GameObjectAI
diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index 8e5652b66ab..a912ed21478 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -366,6 +366,43 @@ bool NonTankTargetSelector::operator()(Unit const* target) const
return target != _source->GetVictim();
}
+bool PowerUsersSelector::operator()(Unit const* target) const
+{
+ if (!_me || !target)
+ return false;
+
+ if (target->GetPowerType() != _power)
+ return false;
+
+ if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
+ return false;
+
+ if (_dist < 0.0f && _me->IsWithinCombatRange(target, -_dist))
+ return false;
+
+ return true;
+}
+
+bool FarthestTargetSelector::operator()(Unit const* target) const
+{
+ if (!_me || !target)
+ return false;
+
+ if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
+ return false;
+
+ if (_inLos && !_me->IsWithinLOSInMap(target))
+ return false;
+
+ return true;
+}
+
void SortByDistanceTo(Unit* reference, std::list<Unit*>& targets)
{
targets.sort(Trinity::ObjectDistanceOrderPred(reference));
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index 6eca66f03ec..5d410485b91 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -97,6 +97,33 @@ struct TC_GAME_API NonTankTargetSelector : public std::unary_function<Unit*, boo
bool _playerOnly;
};
+// Simple selector for units using mana
+struct TC_GAME_API PowerUsersSelector
+{
+public:
+ PowerUsersSelector(Unit const* unit, Powers power, float dist, bool playerOnly) : _me(unit), _power(power), _dist(dist), _playerOnly(playerOnly) { }
+ bool operator()(Unit const* target) const;
+
+private:
+ Unit const* _me;
+ Powers const _power;
+ float const _dist;
+ bool const _playerOnly;
+};
+
+struct TC_GAME_API FarthestTargetSelector
+{
+public:
+ FarthestTargetSelector(Unit const* unit, float dist, bool playerOnly, bool inLos) : _me(unit), _dist(dist), _playerOnly(playerOnly), _inLos(inLos) {}
+ bool operator()(Unit const* target) const;
+
+private:
+ const Unit* _me;
+ float _dist;
+ bool _playerOnly;
+ bool _inLos;
+};
+
TC_GAME_API void SortByDistanceTo(Unit* reference, std::list<Unit*>& targets);
class TC_GAME_API UnitAI
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 254f1316204..e72cffb8ed5 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -26,6 +26,7 @@
#include "PetDefines.h"
#include "Player.h"
#include "ScriptMgr.h"
+#include "Vehicle.h"
SmartAI::SmartAI(Creature* c) : CreatureAI(c)
{
@@ -37,6 +38,7 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
mCurrentWPID = 0;//first wp id is 1 !!
mWPReached = false;
mWPPauseTimer = 0;
+ mEscortNPCFlags = 0;
mLastWP = nullptr;
mCanRepeatPath = false;
@@ -70,6 +72,8 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
mInvincibilityHpLevel = 0;
mJustReset = false;
+ mConditionsTimer = 0;
+ mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, c->GetEntry());
}
bool SmartAI::IsAIControlled() const
@@ -77,7 +81,7 @@ bool SmartAI::IsAIControlled() const
return !mIsCharmed;
}
-void SmartAI::UpdateDespawn(const uint32 diff)
+void SmartAI::UpdateDespawn(uint32 diff)
{
if (mDespawnState <= 1 || mDespawnState > 3)
return;
@@ -114,28 +118,39 @@ WayPoint* SmartAI::GetNextWayPoint()
return nullptr;
}
-void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* /*invoker*/)
+void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
{
if (me->IsInCombat())// no wp movement in combat
{
TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement while in combat, ignoring.", me->GetEntry());
return;
}
+
if (HasEscortState(SMART_ESCORT_ESCORTING))
StopPath();
+
if (path)
+ {
if (!LoadPath(path))
return;
+ }
+
if (!mWayPoints || mWayPoints->empty())
return;
- AddEscortState(SMART_ESCORT_ESCORTING);
- mCanRepeatPath = repeat;
-
- SetRun(run);
-
if (WayPoint* wp = GetNextWayPoint())
{
+ AddEscortState(SMART_ESCORT_ESCORTING);
+ mCanRepeatPath = repeat;
+
+ SetRun(run);
+
+ if (invoker && invoker->GetTypeId() == TYPEID_PLAYER)
+ {
+ mEscortNPCFlags = me->m_unitData->NpcFlags[0];
+ me->SetNpcFlags((NPCFlags)0);
+ }
+
mLastOOCPos = me->GetPosition();
me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, wp->id, GetScript()->GetPathId());
@@ -205,6 +220,12 @@ void SmartAI::EndPath(bool fail)
mWPPauseTimer = 0;
mLastWP = nullptr;
+ if (mEscortNPCFlags)
+ {
+ me->SetNpcFlags((NPCFlags)mEscortNPCFlags);
+ mEscortNPCFlags = 0;
+ }
+
if (mCanRepeatPath)
{
if (IsAIControlled())
@@ -281,12 +302,21 @@ void SmartAI::UpdatePath(const uint32 diff)
return;
if (mEscortInvokerCheckTimer < diff)
{
+ // Escort failed, no players in range
if (!IsEscortInvokerInRange())
{
- StopPath(mDespawnTime, mEscortQuestID, true);
+ StopPath(0, mEscortQuestID, true);
+
+ // allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying
+ GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, me);
+ me->DespawnOrUnsummon(1);
+ return;
}
mEscortInvokerCheckTimer = 1000;
- } else mEscortInvokerCheckTimer -= diff;
+ }
+ else
+ mEscortInvokerCheckTimer -= diff;
+
// handle pause
if (HasEscortState(SMART_ESCORT_PAUSED))
{
@@ -306,10 +336,11 @@ void SmartAI::UpdatePath(const uint32 diff)
mWPReached = true;
}
mWPPauseTimer = 0;
- } else {
- mWPPauseTimer -= diff;
}
+ else
+ mWPPauseTimer -= diff;
}
+
if (HasEscortState(SMART_ESCORT_RETURNING))
{
if (mWPReached)//reached OOC WP
@@ -320,8 +351,10 @@ void SmartAI::UpdatePath(const uint32 diff)
mWPReached = false;
}
}
+
if ((!me->HasReactState(REACT_PASSIVE) && me->IsInCombat()) || HasEscortState(SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING))
return;
+
// handle next wp
if (mWPReached)//reached WP
{
@@ -340,6 +373,7 @@ void SmartAI::UpdatePath(const uint32 diff)
void SmartAI::UpdateAI(uint32 diff)
{
+ CheckConditions(diff);
GetScript()->OnUpdate(diff);
UpdatePath(diff);
UpdateDespawn(diff);
@@ -351,7 +385,7 @@ void SmartAI::UpdateAI(uint32 diff)
{
if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, true))
{
- StopFollow();
+ StopFollow(true);
return;
}
@@ -376,18 +410,19 @@ bool SmartAI::IsEscortInvokerInRange()
ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS);
if (targets)
{
+ float checkDist = me->GetInstanceScript() ? SMART_ESCORT_MAX_PLAYER_DIST * 2 : SMART_ESCORT_MAX_PLAYER_DIST;
if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin())))
{
Player* player = (*targets->begin())->ToPlayer();
- if (me->GetDistance(player) <= SMART_ESCORT_MAX_PLAYER_DIST)
- return true;
+ if (me->GetDistance(player) <= checkDist)
+ return true;
if (Group* group = player->GetGroup())
{
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
{
Player* groupGuy = groupRef->GetSource();
- if (groupGuy->IsInMap(player) && me->GetDistance(groupGuy) <= SMART_ESCORT_MAX_PLAYER_DIST)
+ if (groupGuy->IsInMap(player) && me->GetDistance(groupGuy) <= checkDist)
return true;
}
}
@@ -398,13 +433,18 @@ bool SmartAI::IsEscortInvokerInRange()
{
if (GetScript()->IsPlayer((*iter)))
{
- if (me->GetDistance((*iter)->ToPlayer()) <= SMART_ESCORT_MAX_PLAYER_DIST)
+ if (me->GetDistance((*iter)->ToPlayer()) <= checkDist)
return true;
}
}
}
+
+ // no valid target found
+ return false;
}
- return true;//escort targets were not set, ignore range check
+
+ // no player invoker was stored, just ignore range check
+ return true;
}
void SmartAI::MovepointReached(uint32 id)
@@ -435,8 +475,15 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
return;
}
+ if (!IsAIControlled())
+ {
+ me->AttackStop();
+ return;
+ }
+
if (!_EnterEvadeMode())
return;
+
me->AddUnitState(UNIT_STATE_EVADE);
GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);//must be after aura clear so we can cast spells from db
@@ -503,7 +550,7 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
return false;
//never attack friendly
- if (me->IsFriendlyTo(who))
+ if (!me->IsValidAssistTarget(who->GetVictim()))
return false;
//too far away and no free sight?
@@ -607,6 +654,14 @@ void SmartAI::JustSummoned(Creature* creature)
void SmartAI::AttackStart(Unit* who)
{
+ // dont allow charmed npcs to act on their own
+ if (!IsAIControlled())
+ {
+ if (who && mCanAutoAttack)
+ me->Attack(who, true);
+ return;
+ }
+
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
{
if (mCanCombatMove)
@@ -674,9 +729,11 @@ void SmartAI::InitializeAI()
{
GetScript()->OnInitialize(me);
if (!me->isDead())
- mJustReset = true;
- JustReachedHome();
- GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
+ {
+ mJustReset = true;
+ JustReachedHome();
+ GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
+ }
}
void SmartAI::OnCharmed(bool apply)
@@ -731,11 +788,16 @@ void SmartAI::SetRun(bool run)
mRun = run;
}
-void SmartAI::SetFly(bool fly)
+void SmartAI::SetDisableGravity(bool fly)
{
me->SetDisableGravity(fly);
}
+void SmartAI::SetCanFly(bool fly)
+{
+ me->SetCanFly(fly);
+}
+
void SmartAI::SetSwim(bool swim)
{
me->SetSwim(swim);
@@ -809,13 +871,13 @@ void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, ui
{
if (!target)
{
- StopFollow();
+ StopFollow(false);
return;
}
mFollowGuid = target->GetGUID();
- mFollowDist = dist >= 0.0f ? dist : PET_FOLLOW_DIST;
- mFollowAngle = angle >= 0.0f ? angle : me->GetFollowAngle();
+ mFollowDist = dist;
+ mFollowAngle = angle;
mFollowArrivedTimer = 1000;
mFollowCredit = credit;
mFollowArrivedEntry = end;
@@ -824,16 +886,8 @@ void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, ui
me->GetMotionMaster()->MoveFollow(target, mFollowDist, mFollowAngle);
}
-void SmartAI::StopFollow()
+void SmartAI::StopFollow(bool complete)
{
- if (Player* player = ObjectAccessor::GetPlayer(*me, mFollowGuid))
- {
- if (!mFollowCreditType)
- player->RewardPlayerAndGroupAtEvent(mFollowCredit, me);
- else
- player->GroupEventHappens(mFollowCredit, me);
- }
-
mFollowGuid.Clear();
mFollowDist = 0;
mFollowAngle = 0;
@@ -841,12 +895,25 @@ void SmartAI::StopFollow()
mFollowArrivedTimer = 1000;
mFollowArrivedEntry = 0;
mFollowCreditType = 0;
- SetDespawnTime(5000);
me->StopMoving();
me->GetMotionMaster()->MoveIdle();
+
+ if (!complete)
+ return;
+
+ if (Player * player = ObjectAccessor::GetPlayer(*me, mFollowGuid))
+ {
+ if (!mFollowCreditType)
+ player->RewardPlayerAndGroupAtEvent(mFollowCredit, me);
+ else
+ player->GroupEventHappens(mFollowCredit, me);
+ }
+
+ SetDespawnTime(5000);
StartDespawn();
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED);
}
+
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker)
{
if (invoker)
@@ -867,6 +934,35 @@ void SmartAI::OnSpellClick(Unit* clicker, bool& result)
GetScript()->ProcessEventsFor(SMART_EVENT_ON_SPELLCLICK, clicker);
}
+void SmartAI::CheckConditions(uint32 diff)
+{
+ if (!mHasConditions)
+ return;
+
+ if (mConditionsTimer <= diff)
+ {
+ if (Vehicle * vehicleKit = me->GetVehicleKit())
+ {
+ for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
+ if (Unit * passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
+ {
+ if (Player * player = passenger->ToPlayer())
+ {
+ if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
+ {
+ player->ExitVehicle();
+ return; // check other pessanger in next tick
+ }
+ }
+ }
+ }
+
+ mConditionsTimer = 1000;
+ }
+ else
+ mConditionsTimer -= diff;
+}
+
int SmartGameObjectAI::Permissible(const GameObject* g)
{
if (g->GetAIName() == "SmartGameObjectAI")
@@ -882,20 +978,25 @@ void SmartGameObjectAI::UpdateAI(uint32 diff)
void SmartGameObjectAI::InitializeAI()
{
GetScript()->OnInitialize(go);
- GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
+ // do not call respawn event if go is not spawned
+ if (go->isSpawned())
+ GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
//Reset();
}
void SmartGameObjectAI::Reset()
{
+ // call respawn event on reset
+ GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
+
GetScript()->OnReset();
}
// Called when a player opens a gossip dialog with the gameobject.
-bool SmartGameObjectAI::GossipHello(Player* player, bool /*isUse*/)
+bool SmartGameObjectAI::GossipHello(Player* player, bool reportUse)
{
TC_LOG_DEBUG("scripts.ai", "SmartGameObjectAI::GossipHello");
- GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player, 0, 0, false, nullptr, go);
+ GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player, uint32(reportUse), 0, false, nullptr, go);
return false;
}
@@ -959,6 +1060,11 @@ void SmartGameObjectAI::EventInform(uint32 eventId)
GetScript()->ProcessEventsFor(SMART_EVENT_GO_EVENT_INFORM, nullptr, eventId);
}
+void SmartGameObjectAI::SpellHit(Unit* unit, const SpellInfo* spellInfo)
+{
+ GetScript()->ProcessEventsFor(SMART_EVENT_SPELLHIT, unit, 0, 0, false, spellInfo);
+}
+
class SmartTrigger : public AreaTriggerScript
{
public:
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index 0fd453c787b..66f37fa8440 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -36,7 +36,7 @@ enum SmartEscortState
enum SmartEscortVars
{
- SMART_ESCORT_MAX_PLAYER_DIST = 50,
+ SMART_ESCORT_MAX_PLAYER_DIST = 60,
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2
};
@@ -64,7 +64,7 @@ class TC_GAME_API SmartAI : public CreatureAI
void SetCombatMove(bool on);
bool CanCombatMove() { return mCanCombatMove; }
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0);
- void StopFollow();
+ void StopFollow(bool complete);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
SmartScript* GetScript() { return &mScript; }
@@ -166,7 +166,9 @@ class TC_GAME_API SmartAI : public CreatureAI
// Makes the creature run/walk
void SetRun(bool run = true);
- void SetFly(bool fly = true);
+ void SetCanFly(bool fly = true);
+
+ void SetDisableGravity(bool disable = true);
void SetSwim(bool swim = true);
@@ -196,6 +198,8 @@ class TC_GAME_API SmartAI : public CreatureAI
void OnSpellClick(Unit* clicker, bool& result) override;
+ void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
+
private:
bool mIsCharmed;
uint32 mFollowCreditType;
@@ -215,6 +219,7 @@ class TC_GAME_API SmartAI : public CreatureAI
uint32 mLastWPIDReached;
bool mWPReached;
uint32 mWPPauseTimer;
+ uint32 mEscortNPCFlags;
WayPoint* mLastWP;
Position mLastOOCPos;//set on enter combat
uint32 GetWPCount() const { return mWayPoints ? uint32(mWayPoints->size()) : 0; }
@@ -230,9 +235,14 @@ class TC_GAME_API SmartAI : public CreatureAI
uint32 mDespawnTime;
uint32 mRespawnTime;
uint32 mDespawnState;
- void UpdateDespawn(const uint32 diff);
+ void UpdateDespawn(uint32 diff);
uint32 mEscortInvokerCheckTimer;
bool mJustReset;
+
+ // Vehicle conditions
+ void CheckConditions(uint32 diff);
+ bool mHasConditions;
+ uint32 mConditionsTimer;
};
class TC_GAME_API SmartGameObjectAI : public GameObjectAI
@@ -247,7 +257,7 @@ class TC_GAME_API SmartGameObjectAI : public GameObjectAI
SmartScript* GetScript() { return &mScript; }
static int Permissible(const GameObject* g);
- bool GossipHello(Player* player, bool isUse) override;
+ bool GossipHello(Player* player, bool reportUse) override;
bool GossipSelect(Player* player, uint32 sender, uint32 action) override;
bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/) override;
bool QuestAccept(Player* player, Quest const* quest) override;
@@ -258,6 +268,7 @@ class TC_GAME_API SmartGameObjectAI : public GameObjectAI
void OnGameEvent(bool start, uint16 eventId) override;
void OnStateChanged(uint32 state, Unit* unit) override;
void EventInform(uint32 eventId) override;
+ void SpellHit(Unit* unit, const SpellInfo* spellInfo) override;
private:
SmartScript mScript;
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index 62d6ff2f881..6d664ddb2dc 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -125,29 +125,23 @@ ObjectList* SmartScript::GetTargetList(uint32 id)
void SmartScript::StoreCounter(uint32 id, uint32 value, uint32 reset)
{
- CounterMap::const_iterator itr = mCounterList.find(id);
+ CounterMap::iterator itr = mCounterList.find(id);
if (itr != mCounterList.end())
{
if (reset == 0)
- value += GetCounterValue(id);
- mCounterList.erase(id);
+ itr->second += value;
+ else
+ itr->second = value;
}
+ else
+ mCounterList.insert(std::make_pair(id, value));
- mCounterList.insert(std::make_pair(id, value));
- ProcessEventsFor(SMART_EVENT_COUNTER_SET);
+ ProcessEventsFor(SMART_EVENT_COUNTER_SET, nullptr, id);
}
-uint32 SmartScript::GetCounterId(uint32 id)
+uint32 SmartScript::GetCounterValue(uint32 id) const
{
- CounterMap::iterator itr = mCounterList.find(id);
- if (itr != mCounterList.end())
- return itr->first;
- return 0;
-}
-
-uint32 SmartScript::GetCounterValue(uint32 id)
-{
- CounterMap::iterator itr = mCounterList.find(id);
+ CounterMap::const_iterator itr = mCounterList.find(id);
if (itr != mCounterList.end())
return itr->second;
return 0;
@@ -256,8 +250,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
case SMART_ACTION_TALK:
{
ObjectList* targets = GetTargets(e, unit);
- Creature* talker = me;
- Player* targetPlayer = nullptr;
+ Creature* talker = e.target.type == 0 ? me : nullptr;
Unit* talkTarget = nullptr;
if (targets)
@@ -267,14 +260,18 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsCreature(*itr) && !(*itr)->ToCreature()->IsPet()) // Prevented sending text to pets.
{
if (e.action.talk.useTalkTarget)
+ {
+ talker = me;
talkTarget = (*itr)->ToCreature();
+ }
else
talker = (*itr)->ToCreature();
break;
}
else if (IsPlayer(*itr))
{
- targetPlayer = (*itr)->ToPlayer();
+ talker = me;
+ talkTarget = (*itr)->ToPlayer();
break;
}
}
@@ -282,18 +279,15 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
delete targets;
}
+ if (!talkTarget)
+ talkTarget = GetLastInvoker();
+
if (!talker)
break;
mTalkerEntry = talker->GetEntry();
mLastTextID = e.action.talk.textGroupID;
mTextTimer = e.action.talk.duration;
-
- if (IsPlayer(GetLastInvoker())) // used for $vars in texts and whisper target
- talkTarget = GetLastInvoker();
- else if (targetPlayer)
- talkTarget = targetPlayer;
-
mUseTextTimer = true;
sCreatureTextMgr->SendChat(talker, uint8(e.action.talk.textGroupID), talkTarget);
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_TALK: talker: %s (%s), textGuid: %s",
@@ -626,8 +620,17 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!targets)
break;
+ if (e.action.cast.targetsLimit > 0 && targets->size() > e.action.cast.targetsLimit)
+ Trinity::Containers::RandomResize(*targets, e.action.cast.targetsLimit);
+
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
{
+ if (go)
+ {
+ // may be nullptr
+ go->CastSpell((*itr)->ToUnit(), e.action.cast.spell);
+ }
+
if (!IsUnit(*itr))
continue;
@@ -710,6 +713,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!targets)
break;
+ if (e.action.cast.targetsLimit > 0 && targets->size() > e.action.cast.targetsLimit)
+ Trinity::Containers::RandomResize(*targets, e.action.cast.targetsLimit);
+
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
{
if (!IsUnit(*itr))
@@ -932,7 +938,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
me->DoFleeToGetAssistance();
- if (e.action.flee.withEmote)
+ if (e.action.fleeAssist.withEmote)
{
Trinity::BroadcastTextBuilder builder(me, CHAT_MSG_MONSTER_EMOTE, BROADCAST_TEXT_FLEE_FOR_ASSIST, me->getGender());
CreatureTextMgr::SendChatPacket(me, builder, CHAT_MSG_MONSTER_EMOTE);
@@ -945,9 +951,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!unit)
break;
- if (IsPlayer(unit) && GetBaseObject())
+ // If invoker was pet or charm
+ Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
+ if (player && GetBaseObject())
{
- unit->ToPlayer()->GroupEventHappens(e.action.quest.quest, GetBaseObject());
+ player->GroupEventHappens(e.action.quest.quest, GetBaseObject());
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_GROUPEVENTHAPPENS: %s, group credit for quest %u",
unit->GetGUID().ToString().c_str(), e.action.quest.quest);
}
@@ -1007,7 +1015,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
ObjectList* targets = GetTargets(e, unit);
if (!targets)
{
- ENSURE_AI(SmartAI, me->AI())->StopFollow();
+ ENSURE_AI(SmartAI, me->AI())->StopFollow(false);
break;
}
@@ -1015,7 +1023,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (IsUnit(*itr))
{
- ENSURE_AI(SmartAI, me->AI())->SetFollow((*itr)->ToUnit(), (float)e.action.follow.dist, (float)e.action.follow.angle, e.action.follow.credit, e.action.follow.entry, e.action.follow.creditType);
+ float angle = e.action.follow.angle > 6 ? (e.action.follow.angle * M_PI / 180.0f) : e.action.follow.angle;
+ ENSURE_AI(SmartAI, me->AI())->SetFollow((*itr)->ToUnit(), float(e.action.follow.dist) + 0.1f, angle, e.action.follow.credit, e.action.follow.entry, e.action.follow.creditType);
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_FOLLOW: %s following target %s",
me->GetGUID().ToString().c_str(), (*itr)->GetGUID().ToString().c_str());
break;
@@ -1192,25 +1201,39 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_SET_IN_COMBAT_WITH_ZONE:
{
- if (me)
- {
- me->SetInCombatWithZone();
- TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: %s", me->GetGUID().ToString().c_str());
- }
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
+ {
+ (*itr)->ToCreature()->SetInCombatWithZone();
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: Creature %s, target: %s", me->GetGUID().ToString().c_str(), (*itr)->GetGUID().ToString().c_str());
+ }
+
+ delete targets;
break;
}
case SMART_ACTION_CALL_FOR_HELP:
{
- if (me)
- {
- me->CallForHelp((float)e.action.callHelp.range);
- if (e.action.callHelp.withEmote)
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
{
- Trinity::BroadcastTextBuilder builder(me, CHAT_MSG_MONSTER_EMOTE, BROADCAST_TEXT_CALL_FOR_HELP, me->getGender());
- CreatureTextMgr::SendChatPacket(me, builder, CHAT_MSG_MONSTER_EMOTE);
+ (*itr)->ToCreature()->CallForHelp((float)e.action.callHelp.range);
+ if (e.action.callHelp.withEmote)
+ {
+ Trinity::BroadcastTextBuilder builder(me, CHAT_MSG_MONSTER_EMOTE, BROADCAST_TEXT_CALL_FOR_HELP, me->getGender());
+ sCreatureTextMgr->SendChatPacket(me, builder, CHAT_MSG_MONSTER_EMOTE);
+ }
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_FOR_HELP: Creature %s, target: %s", me->GetGUID().ToString().c_str(), (*itr)->GetGUID().ToString().c_str());
}
- TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_FOR_HELP: %s", me->GetGUID().ToString().c_str());
- }
+
+ delete targets;
break;
}
case SMART_ACTION_SET_SHEATH:
@@ -1318,23 +1341,6 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL:
{
- if (!me)
- break;
-
- SmartAI* ai = CAST_AI(SmartAI, me->AI());
-
- if (!ai)
- break;
-
- if (e.action.invincHP.percent)
- ai->SetInvincibilityHpLevel(me->CountPctFromMaxHealth(e.action.invincHP.percent));
- else
- ai->SetInvincibilityHpLevel(e.action.invincHP.minHP);
-
- break;
- }
- case SMART_ACTION_SET_DATA:
- {
ObjectList* targets = GetTargets(e, unit);
if (!targets)
break;
@@ -1342,9 +1348,16 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
{
if (IsCreature(*itr))
- (*itr)->ToCreature()->AI()->SetData(e.action.setData.field, e.action.setData.data);
- else if (IsGameObject(*itr))
- (*itr)->ToGameObject()->AI()->SetData(e.action.setData.field, e.action.setData.data);
+ {
+ SmartAI* ai = CAST_AI(SmartAI, (*itr)->ToCreature()->AI());
+ if (!ai)
+ continue;
+
+ if (e.action.invincHP.percent)
+ ai->SetInvincibilityHpLevel((*itr)->ToCreature()->CountPctFromMaxHealth(e.action.invincHP.percent));
+ else
+ ai->SetInvincibilityHpLevel(e.action.invincHP.minHP);
+ }
}
delete targets;
@@ -1380,15 +1393,27 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_SET_VISIBILITY:
{
- if (me)
- me->SetVisible(e.action.visibility.state != 0);
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsUnit(*itr))
+ (*itr)->ToUnit()->SetVisible(e.action.visibility.state ? true : false);
+
+ delete targets;
break;
}
case SMART_ACTION_SET_ACTIVE:
{
- if (WorldObject* baseObj = GetBaseObject())
- baseObj->setActive(e.action.active.state != 0);
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ (*itr)->setActive(e.action.active.state ? true : false);
+
+ delete targets;
break;
}
case SMART_ACTION_ATTACK_START:
@@ -1400,14 +1425,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!targets)
break;
- for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
- {
- if (IsUnit(*itr))
- {
- me->AI()->AttackStart((*itr)->ToUnit());
- break;
- }
- }
+ // attack random target
+ if (Unit * target = Trinity::Containers::SelectRandomContainerElement(*targets)->ToUnit())
+ me->AI()->AttackStart(target);
delete targets;
break;
@@ -1457,8 +1477,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
{
- if (!IsUnit(*itr))
- continue;
+ // allow gameobjects to summon gameobjects
+ //if (!IsUnit(*itr))
+ // continue;
Position pos = (*itr)->GetPositionWithOffset(Position(e.target.x, e.target.y, e.target.z, e.target.o));
summoner->SummonGameObject(e.action.summonGO.entry, pos, QuaternionData::fromEulerAnglesZYX(pos.GetOrientation(), 0.f, 0.f), e.action.summonGO.despawnTime);
@@ -1552,12 +1573,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
delete targets;
break;
}
- case SMART_ACTION_SET_FLY:
+ case SMART_ACTION_SET_DISABLE_GRAVITY:
+ {
+ if (!IsSmart())
+ break;
+
+ ENSURE_AI(SmartAI, me->AI())->SetDisableGravity(e.action.setDisableGravity.disable != 0);
+ break;
+ }
+ case SMART_ACTION_SET_CAN_FLY:
{
if (!IsSmart())
break;
- ENSURE_AI(SmartAI, me->AI())->SetFly(e.action.setFly.fly != 0);
+ ENSURE_AI(SmartAI, me->AI())->SetCanFly(e.action.setFly.fly != 0);
break;
}
case SMART_ACTION_SET_RUN:
@@ -1613,8 +1642,25 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
bool run = e.action.wpStart.run != 0;
uint32 entry = e.action.wpStart.pathID;
bool repeat = e.action.wpStart.repeat != 0;
+
+ // ensure that SMART_ESCORT_TARGETS contains at least one player reference
+ bool stored = false;
ObjectList* targets = GetTargets(e, unit);
- StoreTargetList(targets, SMART_ESCORT_TARGETS);
+ if (targets)
+ {
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ {
+ if (IsPlayer(*itr))
+ {
+ stored = true;
+ StoreTargetList(targets, SMART_ESCORT_TARGETS);
+ break;
+ }
+ }
+ if (!stored)
+ delete targets;
+ }
+
me->SetReactState((ReactStates)e.action.wpStart.reactState);
ENSURE_AI(SmartAI, me->AI())->StartPath(run, entry, repeat, unit);
@@ -1649,7 +1695,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!IsSmart())
break;
- ENSURE_AI(SmartAI, me->AI())->ResumePath();
+ ENSURE_AI(SmartAI, me->AI())->SetWPPauseTimer(0);
break;
}
case SMART_ACTION_SET_ORIENTATION:
@@ -1695,19 +1741,19 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
WorldObject* target = nullptr;
- if (e.GetTargetType() == SMART_TARGET_CREATURE_RANGE || e.GetTargetType() == SMART_TARGET_CREATURE_GUID ||
+ /*if (e.GetTargetType() == SMART_TARGET_CREATURE_RANGE || e.GetTargetType() == SMART_TARGET_CREATURE_GUID ||
e.GetTargetType() == SMART_TARGET_CREATURE_DISTANCE || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_RANGE ||
e.GetTargetType() == SMART_TARGET_GAMEOBJECT_GUID || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_DISTANCE ||
e.GetTargetType() == SMART_TARGET_CLOSEST_CREATURE || e.GetTargetType() == SMART_TARGET_CLOSEST_GAMEOBJECT ||
e.GetTargetType() == SMART_TARGET_OWNER_OR_SUMMONER || e.GetTargetType() == SMART_TARGET_ACTION_INVOKER ||
- e.GetTargetType() == SMART_TARGET_CLOSEST_ENEMY || e.GetTargetType() == SMART_TARGET_CLOSEST_FRIENDLY)
+ e.GetTargetType() == SMART_TARGET_CLOSEST_ENEMY || e.GetTargetType() == SMART_TARGET_CLOSEST_FRIENDLY)*/
{
- ObjectList* targets = GetTargets(e, unit);
- if (!targets)
- break;
-
- target = targets->front();
- delete targets;
+ if (ObjectList * targets = GetTargets(e, unit))
+ {
+ // we want to move to random element
+ target = Trinity::Containers::SelectRandomContainerElement(*targets);
+ delete targets;
+ }
}
if (!target)
@@ -1725,7 +1771,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
target->GetPosition(x, y, z);
if (e.action.MoveToPos.ContactDistance > 0)
target->GetContactPoint(me, x, y, z, e.action.MoveToPos.ContactDistance);
- me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, x, y, z, e.action.MoveToPos.disablePathfinding == 0);
+ me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, x + e.target.x, y + e.target.y, z + e.target.z, e.action.MoveToPos.disablePathfinding == 0);
}
break;
}
@@ -1740,7 +1786,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (IsCreature(*itr))
(*itr)->ToCreature()->Respawn();
else if (IsGameObject(*itr))
- (*itr)->ToGameObject()->SetRespawnTime(e.action.RespawnTarget.goRespawnTime);
+ {
+ // do not modify respawndelay of already spawned gameobjects
+ if ((*itr)->ToGameObject()->isSpawnedByDefault())
+ (*itr)->ToGameObject()->Respawn();
+ else
+ (*itr)->ToGameObject()->SetRespawnTime(e.action.RespawnTarget.goRespawnTime);
+ }
}
delete targets;
@@ -1834,6 +1886,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_TRIGGER_TIMED_EVENT:
ProcessEventsFor((SMART_EVENT)SMART_EVENT_TIMED_EVENT_TRIGGERED, nullptr, e.action.timeEvent.id);
+
+ // remove this event if not repeatable
+ if (e.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE)
+ mRemIDs.push_back(e.action.timeEvent.id);
break;
case SMART_ACTION_REMOVE_TIMED_EVENT:
mRemIDs.push_back(e.action.timeEvent.id);
@@ -2639,6 +2695,109 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
ENSURE_AI(SmartAI, me->AI())->SetEvadeDisabled(e.action.disableEvade.disable != 0);
break;
}
+ case SMART_ACTION_REMOVE_AURAS_BY_TYPE: // can be used to exit vehicle for example
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsUnit(*itr))
+ (*itr)->ToUnit()->RemoveAurasByType((AuraType)e.action.auraType.type);
+
+ delete targets;
+ break;
+ }
+ case SMART_ACTION_SET_SIGHT_DIST:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
+ (*itr)->ToCreature()->m_SightDistance = e.action.sightDistance.dist;
+
+ delete targets;
+ break;
+ }
+ case SMART_ACTION_FLEE:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
+ (*itr)->ToCreature()->GetMotionMaster()->MoveFleeing(me, e.action.flee.fleeTime);
+
+ delete targets;
+ break;
+ }
+ case SMART_ACTION_ADD_THREAT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsUnit(*itr))
+ me->AddThreat((*itr)->ToUnit(), (float)e.action.threatPCT.threatINC - (float)e.action.threatPCT.threatDEC);
+
+ delete targets;
+ break;
+ }
+ case SMART_ACTION_LOAD_EQUIPMENT:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsCreature(*itr))
+ (*itr)->ToCreature()->LoadEquipment(e.action.loadEquipment.id, e.action.loadEquipment.force != 0);
+
+ delete targets;
+ break;
+ }
+ case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT:
+ {
+ uint32 eventId = urand(e.action.randomTimedEvent.minId, e.action.randomTimedEvent.maxId);
+ ProcessEventsFor((SMART_EVENT)SMART_EVENT_TIMED_EVENT_TRIGGERED, NULL, eventId);
+ break;
+ }
+
+ case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsUnit(*itr))
+ (*itr)->ToUnit()->RemoveAllGameObjects();
+
+ delete targets;
+ break;
+ }
+ case SMART_ACTION_STOP_MOTION:
+ {
+ ObjectList* targets = GetTargets(e, unit);
+ if (!targets)
+ break;
+
+ for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
+ if (IsUnit(*itr))
+ {
+ if (e.action.stopMotion.stopMovement)
+ (*itr)->ToUnit()->StopMoving();
+ if (e.action.stopMotion.movementExpired)
+ (*itr)->ToUnit()->GetMotionMaster()->MovementExpired();
+ }
+
+ delete targets;
+ break;
+ }
case SMART_ACTION_PLAY_ANIMKIT:
{
ObjectList* targets = GetTargets(e, unit);
@@ -2714,10 +2873,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
void SmartScript::ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit, uint32 var0, uint32 var1, bool bvar, const SpellInfo* spell, GameObject* gob, std::string const& varString)
{
+ // We may want to execute action rarely and because of this if condition is not fulfilled the action will be rechecked in a long time
if (sConditionMgr->IsObjectMeetingSmartEventConditions(e.entryOrGuid, e.event_id, e.source_type, unit, GetBaseObject()))
+ {
ProcessAction(e, unit, var0, var1, bvar, spell, gob, varString);
-
- RecalcTimer(e, min, max);
+ RecalcTimer(e, min, max);
+ }
+ else
+ RecalcTimer(e, std::min<uint32>(min, 5000), std::min<uint32>(min, 5000));
}
void SmartScript::InstallTemplate(SmartScriptHolder const& e)
@@ -2854,23 +3017,58 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
break;
case SMART_TARGET_HOSTILE_SECOND_AGGRO:
if (me)
- if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1))
+ {
+ if (e.target.hostilRandom.powerType)
+ {
+ if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
+ l->push_back(u);
+ }
+ else if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
l->push_back(u);
+ }
break;
case SMART_TARGET_HOSTILE_LAST_AGGRO:
if (me)
- if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0))
+ {
+ if (e.target.hostilRandom.powerType)
+ {
+ if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
+ l->push_back(u);
+ }
+ else if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
l->push_back(u);
+ }
break;
case SMART_TARGET_HOSTILE_RANDOM:
if (me)
- if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ if (e.target.hostilRandom.powerType)
+ {
+ if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
+ l->push_back(u);
+ }
+ else if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
l->push_back(u);
+ }
break;
case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP:
if (me)
- if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1))
+ {
+ if (e.target.hostilRandom.powerType)
+ {
+ if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
+ l->push_back(u);
+ }
+ else if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
+ l->push_back(u);
+ }
+ break;
+ case SMART_TARGET_FARTHEST:
+ if (me)
+ {
+ if (Unit * u = me->AI()->SelectTarget(SELECT_TARGET_FARTHEST, 0, FarthestTargetSelector(me, (float)e.target.farthest.maxDist, e.target.farthest.playerOnly != 0, e.target.farthest.isInLos != 0)))
l->push_back(u);
+ }
break;
case SMART_TARGET_ACTION_INVOKER:
if (scriptTrigger)
@@ -3034,7 +3232,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
l->assign(objectList->begin(), objectList->end());
}
- return l;
+ break;
}
case SMART_TARGET_CLOSEST_CREATURE:
{
@@ -3050,8 +3248,8 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
}
case SMART_TARGET_CLOSEST_PLAYER:
{
- if (me)
- if (Player* target = me->SelectNearestPlayer(float(e.target.playerDistance.dist)))
+ if (WorldObject * obj = GetBaseObject())
+ if (Player * target = obj->SelectNearestPlayer(float(e.target.playerDistance.dist)))
l->push_back(target);
break;
}
@@ -3072,6 +3270,21 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
if (Unit* owner = ObjectAccessor::GetUnit(*me, charmerOrOwnerGuid))
l->push_back(owner);
}
+ else if (go)
+ {
+ if (Unit * owner = ObjectAccessor::GetUnit(*go, go->GetOwnerGUID()))
+ l->push_back(owner);
+ }
+
+ // Get owner of owner
+ if (e.target.owner.useCharmerOrOwner && !l->empty())
+ {
+ Unit* owner = l->front()->ToUnit();
+ l->clear();
+
+ if (Unit * base = ObjectAccessor::GetUnit(*owner, owner->GetCharmerOrOwnerGUID()))
+ l->push_back(base);
+ }
break;
}
case SMART_TARGET_THREAT_LIST:
@@ -3081,7 +3294,8 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
ThreatContainer::StorageType threatList = me->getThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
if (Unit* temp = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
- l->push_back(temp);
+ if (e.target.hostilRandom.maxDist == 0 || me->IsWithinCombatRange(temp, (float)e.target.hostilRandom.maxDist))
+ l->push_back(temp);
}
break;
}
@@ -3232,6 +3446,8 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max))
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->GetVictim());
+ else // make it predictable
+ RecalcTimer(e, 500, 500);
break;
}
case SMART_EVENT_VICTIM_CASTING:
@@ -3259,7 +3475,12 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealth.radius, e.event.friendlyHealth.hpDeficit);
if (!target || !target->IsInCombat())
+ {
+ // if there are at least two same npcs, they will perform the same action immediately even if this is useless...
+ RecalcTimer(e, 1000, 3000);
return;
+ }
+
ProcessTimedAction(e, e.event.friendlyHealth.repeatMin, e.event.friendlyHealth.repeatMax, target);
break;
}
@@ -3271,8 +3492,12 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
std::list<Creature*> pList;
DoFindFriendlyCC(pList, (float)e.event.friendlyCC.radius);
if (pList.empty())
+ {
+ // if there are at least two same npcs, they will perform the same action immediately even if this is useless...
+ RecalcTimer(e, 1000, 3000);
return;
- ProcessTimedAction(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax, *pList.begin());
+ }
+ ProcessTimedAction(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax, Trinity::Containers::SelectRandomContainerElement(pList));
break;
}
case SMART_EVENT_FRIENDLY_MISSING_BUFF:
@@ -3283,7 +3508,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (pList.empty())
return;
- ProcessTimedAction(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax, *pList.begin());
+ ProcessTimedAction(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax, Trinity::Containers::SelectRandomContainerElement(pList));
break;
}
case SMART_EVENT_HAS_AURA:
@@ -3329,11 +3554,15 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
case SMART_EVENT_JUST_SUMMONED:
case SMART_EVENT_RESET:
case SMART_EVENT_JUST_CREATED:
- case SMART_EVENT_GOSSIP_HELLO:
case SMART_EVENT_FOLLOW_COMPLETED:
case SMART_EVENT_ON_SPELLCLICK:
ProcessAction(e, unit, var0, var1, bvar, spell, gob);
break;
+ case SMART_EVENT_GOSSIP_HELLO:
+ if (e.event.gossipHello.noReportUse && var0)
+ return;
+ ProcessAction(e, unit, var0, var1, bvar, spell, gob);
+ break;
case SMART_EVENT_IS_BEHIND_TARGET:
{
if (!me)
@@ -3476,6 +3705,13 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
break;
}
case SMART_EVENT_SUMMON_DESPAWNED:
+ {
+ if (e.event.summoned.creature && e.event.summoned.creature != var0)
+ return;
+ ProcessAction(e, unit, var0);
+ RecalcTimer(e, e.event.summoned.cooldownMin, e.event.summoned.cooldownMax);
+ break;
+ }
case SMART_EVENT_INSTANCE_PLAYER_ENTER:
{
if (e.event.instancePlayerEnter.team && var0 != e.event.instancePlayerEnter.team)
@@ -3690,8 +3926,10 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
break;
}
case SMART_EVENT_COUNTER_SET:
- if (GetCounterId(e.event.counter.id) != 0 && GetCounterValue(e.event.counter.id) == e.event.counter.value)
- ProcessTimedAction(e, e.event.counter.cooldownMin, e.event.counter.cooldownMax);
+ if (e.event.counter.id != var0 || GetCounterValue(e.event.counter.id) != e.event.counter.value)
+ return;
+
+ ProcessTimedAction(e, e.event.counter.cooldownMin, e.event.counter.cooldownMax);
break;
case SMART_EVENT_SCENE_START:
case SMART_EVENT_SCENE_CANCEL:
@@ -3772,7 +4010,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
// Delay flee for assist event if stunned or rooted
if (e.GetActionType() == SMART_ACTION_FLEE_FOR_ASSIST)
{
- if (me && me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
+ if (me && me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_LOST_CONTROL))
{
e.timer = 1;
return;
@@ -3843,7 +4081,7 @@ void SmartScript::RemoveStoredEvent(uint32 id)
{
if (!mStoredEvents.empty())
{
- for (SmartAIEventList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
+ for (auto i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
{
if (i->event_id == id)
{
@@ -3916,8 +4154,14 @@ void SmartScript::OnUpdate(uint32 const diff)
UpdateTimer(*i, diff);
if (!mStoredEvents.empty())
- for (SmartAIEventList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
- UpdateTimer(*i, diff);
+ {
+ SmartAIEventStoredList::iterator i, icurr;
+ for (i = mStoredEvents.begin(); i != mStoredEvents.end();)
+ {
+ icurr = i++;
+ UpdateTimer(*icurr, diff);
+ }
+ }
bool needCleanup = true;
if (!mTimedActionList.empty())
@@ -3939,11 +4183,12 @@ void SmartScript::OnUpdate(uint32 const diff)
if (!mRemIDs.empty())
{
- for (std::list<uint32>::iterator i = mRemIDs.begin(); i != mRemIDs.end(); ++i)
- {
- RemoveStoredEvent((*i));
- }
+ for (auto i : mRemIDs)
+ RemoveStoredEvent(i);
+
+ mRemIDs.clear();
}
+
if (mUseTextTimer && me)
{
if (mTextTimer < diff)
@@ -4106,6 +4351,7 @@ Unit* SmartScript::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
return nullptr;
Unit* unit = nullptr;
+
Trinity::MostHPMissingInRange u_check(me, range, MinHPDiff);
Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check);
Cell::VisitGridObjects(me, searcher, range);
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
index 4e00c286952..bc6add0a058 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.h
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -79,8 +79,7 @@ class TC_GAME_API SmartScript
ObjectList* GetTargetList(uint32 id);
void StoreCounter(uint32 id, uint32 value, uint32 reset);
- uint32 GetCounterId(uint32 id);
- uint32 GetCounterValue(uint32 id);
+ uint32 GetCounterValue(uint32 id) const;
GameObject* FindGameObjectNear(WorldObject* searchObject, ObjectGuid::LowType guid) const;
Creature* FindCreatureNear(WorldObject* searchObject, ObjectGuid::LowType guid) const;
@@ -98,23 +97,27 @@ class TC_GAME_API SmartScript
CounterMap mCounterList;
private:
- void IncPhase(int32 p = 1)
+ void IncPhase(uint32 p)
{
- if (p >= 0)
- mEventPhase += (uint32)p;
- else
- DecPhase(-p);
+ // protect phase from overflowing
+ mEventPhase = std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p);
}
- void DecPhase(int32 p = 1)
+ void DecPhase(uint32 p)
{
- if (mEventPhase > (uint32)p)
- mEventPhase -= (uint32)p;
- else
+ if (p >= mEventPhase)
mEventPhase = 0;
+ else
+ mEventPhase -= p;
+ }
+
+ bool IsInPhase(uint32 p) const
+ {
+ if (mEventPhase == 0)
+ return false;
+ return ((1 << (mEventPhase - 1)) & p) != 0;
}
- bool IsInPhase(uint32 p) const { return ((1 << (mEventPhase - 1)) & p) != 0; }
void SetPhase(uint32 p = 0) { mEventPhase = p; }
SmartAIEventList mEvents;
@@ -131,8 +134,8 @@ class TC_GAME_API SmartScript
uint32 mEventPhase;
uint32 mPathId;
- SmartAIEventList mStoredEvents;
- std::list<uint32> mRemIDs;
+ SmartAIEventStoredList mStoredEvents;
+ std::vector<uint32> mRemIDs;
uint32 mTextTimer;
uint32 mLastTextID;
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
index 39e5759e3a7..e09bed95f8f 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp
@@ -235,7 +235,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
temp.event_id = fields[2].GetUInt16();
temp.link = fields[3].GetUInt16();
temp.event.type = (SMART_EVENT)fields[4].GetUInt8();
- temp.event.event_phase_mask = fields[5].GetUInt8();
+ temp.event.event_phase_mask = fields[5].GetUInt16();
temp.event.event_chance = fields[6].GetUInt8();
temp.event.event_flags = fields[7].GetUInt16();
@@ -272,6 +272,50 @@ void SmartAIMgr::LoadSmartAIFromDB()
if (!IsEventValid(temp))
continue;
+ // specific check for timed events
+ switch (temp.event.type)
+ {
+ case SMART_EVENT_UPDATE:
+ case SMART_EVENT_UPDATE_OOC:
+ case SMART_EVENT_UPDATE_IC:
+ case SMART_EVENT_HEALT_PCT:
+ case SMART_EVENT_TARGET_HEALTH_PCT:
+ case SMART_EVENT_MANA_PCT:
+ case SMART_EVENT_TARGET_MANA_PCT:
+ case SMART_EVENT_RANGE:
+ case SMART_EVENT_FRIENDLY_HEALTH:
+ case SMART_EVENT_FRIENDLY_HEALTH_PCT:
+ case SMART_EVENT_FRIENDLY_MISSING_BUFF:
+ case SMART_EVENT_HAS_AURA:
+ case SMART_EVENT_TARGET_BUFFED:
+ if (temp.event.minMaxRepeat.repeatMin == 0 && temp.event.minMaxRepeat.repeatMax == 0 && !(temp.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && temp.source_type != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST)
+ {
+ temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Entry " SI64FMTD " SourceType %u, Event %u, Missing Repeat flag.",
+ temp.entryOrGuid, temp.GetScriptType(), temp.event_id);
+ }
+ break;
+ case SMART_EVENT_VICTIM_CASTING:
+ case SMART_EVENT_IS_BEHIND_TARGET:
+ if (temp.event.minMaxRepeat.min == 0 && temp.event.minMaxRepeat.max == 0 && !(temp.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && temp.source_type != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST)
+ {
+ temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Entry " SI64FMTD " SourceType %u, Event %u, Missing Repeat flag.",
+ temp.entryOrGuid, temp.GetScriptType(), temp.event_id);
+ }
+ break;
+ case SMART_EVENT_FRIENDLY_IS_CC:
+ if (temp.event.friendlyCC.repeatMin == 0 && temp.event.friendlyCC.repeatMax == 0 && !(temp.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && temp.source_type != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST)
+ {
+ temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
+ TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Entry " SI64FMTD " SourceType %u, Event %u, Missing Repeat flag.",
+ temp.entryOrGuid, temp.GetScriptType(), temp.event_id);
+ }
+ break;
+ default:
+ break;
+ }
+
// creature entry / guid not found in storage, create empty event list for it and increase counters
if (mEventMap[source_type].find(temp.entryOrGuid) == mEventMap[source_type].end())
{
@@ -427,6 +471,7 @@ bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e)
case SMART_TARGET_CLOSEST_FRIENDLY:
case SMART_TARGET_STORED:
case SMART_TARGET_LOOT_RECIPIENTS:
+ case SMART_TARGET_FARTHEST:
case SMART_TARGET_VEHICLE_ACCESSORY:
break;
default:
@@ -1060,6 +1105,12 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
}
break;
}
+ case SMART_ACTION_CROSS_CAST:
+ {
+ if (!IsSpellValid(e, e.action.crossCast.spell))
+ return false;
+ break;
+ }
case SMART_ACTION_ADD_AURA:
case SMART_ACTION_INVOKER_CAST:
if (!IsSpellValid(e, e.action.cast.spell))
@@ -1224,20 +1275,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return false;
}
break;
- case SMART_ACTION_SET_COUNTER:
- if (e.action.setCounter.counterId == 0)
- {
- TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry " SI64FMTD " SourceType %u Event %u Action %u uses wrong counterId %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setCounter.counterId);
- return false;
- }
-
- if (e.action.setCounter.value == 0)
- {
- TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry " SI64FMTD " SourceType %u Event %u Action %u uses wrong value %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setCounter.value);
- return false;
- }
-
- break;
case SMART_ACTION_INSTALL_AI_TEMPLATE:
if (e.action.installTtemplate.id >= SMARTAI_TEMPLATE_END)
{
@@ -1413,6 +1450,15 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
break;
}
+ case SMART_ACTION_REMOVE_AURAS_BY_TYPE:
+ {
+ if (e.action.auraType.type >= TOTAL_AURAS)
+ {
+ TC_LOG_ERROR("sql.sql", "Entry " SI64FMTD " SourceType %u Event %u Action %u uses invalid data type %u (value range 0-TOTAL_AURAS), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.auraType.type);
+ return false;
+ }
+ break;
+ }
case SMART_ACTION_START_CLOSEST_WAYPOINT:
case SMART_ACTION_FOLLOW:
case SMART_ACTION_SET_ORIENTATION:
@@ -1437,7 +1483,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_SET_DATA:
case SMART_ACTION_SET_VISIBILITY:
case SMART_ACTION_WP_PAUSE:
- case SMART_ACTION_SET_FLY:
+ case SMART_ACTION_SET_DISABLE_GRAVITY:
+ case SMART_ACTION_SET_CAN_FLY:
case SMART_ACTION_SET_RUN:
case SMART_ACTION_SET_SWIM:
case SMART_ACTION_FORCE_DESPAWN:
@@ -1458,7 +1505,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_SET_NPC_FLAG:
case SMART_ACTION_ADD_NPC_FLAG:
case SMART_ACTION_REMOVE_NPC_FLAG:
- case SMART_ACTION_CROSS_CAST:
case SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST:
case SMART_ACTION_RANDOM_MOVE:
case SMART_ACTION_SET_UNIT_FIELD_BYTES_1:
@@ -1483,6 +1529,13 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_MOVE_OFFSET:
case SMART_ACTION_SET_CORPSE_DELAY:
case SMART_ACTION_DISABLE_EVADE:
+ case SMART_ACTION_SET_SIGHT_DIST:
+ case SMART_ACTION_FLEE:
+ case SMART_ACTION_ADD_THREAT:
+ case SMART_ACTION_LOAD_EQUIPMENT:
+ case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT:
+ case SMART_ACTION_SET_COUNTER:
+ case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS:
break;
default:
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry " SI64FMTD " SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
index 8ced59ccc65..fe1eb7cf516 100644
--- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h
+++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h
@@ -65,9 +65,12 @@ enum SMART_EVENT_PHASE
SMART_EVENT_PHASE_7 = 7,
SMART_EVENT_PHASE_8 = 8,
SMART_EVENT_PHASE_9 = 9,
- SMART_EVENT_PHASE_MAX = 10,
+ SMART_EVENT_PHASE_10 = 10,
+ SMART_EVENT_PHASE_11 = 11,
+ SMART_EVENT_PHASE_12 = 12,
+ SMART_EVENT_PHASE_MAX = 13,
- SMART_EVENT_PHASE_COUNT = 9
+ SMART_EVENT_PHASE_COUNT = 12
};
enum SMART_EVENT_PHASE_BITS
@@ -82,7 +85,12 @@ enum SMART_EVENT_PHASE_BITS
SMART_EVENT_PHASE_7_BIT = 64,
SMART_EVENT_PHASE_8_BIT = 128,
SMART_EVENT_PHASE_9_BIT = 256,
- SMART_EVENT_PHASE_ALL = SMART_EVENT_PHASE_1_BIT + SMART_EVENT_PHASE_2_BIT + SMART_EVENT_PHASE_3_BIT + SMART_EVENT_PHASE_4_BIT + SMART_EVENT_PHASE_5_BIT + SMART_EVENT_PHASE_6_BIT + SMART_EVENT_PHASE_7_BIT + SMART_EVENT_PHASE_8_BIT + SMART_EVENT_PHASE_9_BIT
+ SMART_EVENT_PHASE_10_BIT = 512,
+ SMART_EVENT_PHASE_11_BIT = 1024,
+ SMART_EVENT_PHASE_12_BIT = 2048,
+ SMART_EVENT_PHASE_ALL = SMART_EVENT_PHASE_1_BIT + SMART_EVENT_PHASE_2_BIT + SMART_EVENT_PHASE_3_BIT + SMART_EVENT_PHASE_4_BIT + SMART_EVENT_PHASE_5_BIT +
+ SMART_EVENT_PHASE_6_BIT + SMART_EVENT_PHASE_7_BIT + SMART_EVENT_PHASE_8_BIT + SMART_EVENT_PHASE_9_BIT + SMART_EVENT_PHASE_10_BIT +
+ SMART_EVENT_PHASE_11_BIT + SMART_EVENT_PHASE_12_BIT
};
const uint32 SmartPhaseMask[SMART_EVENT_PHASE_COUNT][2] =
@@ -95,7 +103,10 @@ const uint32 SmartPhaseMask[SMART_EVENT_PHASE_COUNT][2] =
{SMART_EVENT_PHASE_6, SMART_EVENT_PHASE_6_BIT },
{SMART_EVENT_PHASE_7, SMART_EVENT_PHASE_7_BIT },
{SMART_EVENT_PHASE_8, SMART_EVENT_PHASE_8_BIT },
- {SMART_EVENT_PHASE_9, SMART_EVENT_PHASE_9_BIT }
+ {SMART_EVENT_PHASE_9, SMART_EVENT_PHASE_9_BIT },
+ {SMART_EVENT_PHASE_10, SMART_EVENT_PHASE_10_BIT },
+ {SMART_EVENT_PHASE_11, SMART_EVENT_PHASE_11_BIT },
+ {SMART_EVENT_PHASE_12, SMART_EVENT_PHASE_12_BIT }
};
enum SMART_EVENT
@@ -164,7 +175,7 @@ enum SMART_EVENT
SMART_EVENT_LINK = 61, // INTERNAL USAGE, no params, used to link together multiple events, does not use any extra resources to iterate event lists needlessly
SMART_EVENT_GOSSIP_SELECT = 62, // menuID, actionID
SMART_EVENT_JUST_CREATED = 63, // none
- SMART_EVENT_GOSSIP_HELLO = 64, // none
+ SMART_EVENT_GOSSIP_HELLO = 64, // noReportUse (for GOs)
SMART_EVENT_FOLLOW_COMPLETED = 65, // none
SMART_EVENT_DUMMY_EFFECT = 66, // spellId, effectIndex
SMART_EVENT_IS_BEHIND_TARGET = 67, // cooldownMin, CooldownMax
@@ -364,6 +375,11 @@ struct SmartEvent
struct
{
+ uint32 noReportUse;
+ } gossipHello;
+
+ struct
+ {
uint32 sender;
uint32 action;
} gossip;
@@ -511,7 +527,7 @@ enum SMART_ACTION
SMART_ACTION_REMOVE_ITEM = 57, // itemID, count
SMART_ACTION_INSTALL_AI_TEMPLATE = 58, // AITemplateID
SMART_ACTION_SET_RUN = 59, // 0/1
- SMART_ACTION_SET_FLY = 60, // 0/1
+ SMART_ACTION_SET_DISABLE_GRAVITY = 60, // 0/1
SMART_ACTION_SET_SWIM = 61, // 0/1
SMART_ACTION_TELEPORT = 62, // mapID,
SMART_ACTION_SET_COUNTER = 63, // id, value, reset (0/1)
@@ -570,7 +586,15 @@ enum SMART_ACTION
SMART_ACTION_SET_CORPSE_DELAY = 116, // timer
SMART_ACTION_DISABLE_EVADE = 117, // 0/1 (1 = disabled, 0 = enabled)
SMART_ACTION_GO_SET_GO_STATE = 118, // state
- // 119 - 127 : 3.3.5 reserved
+ SMART_ACTION_SET_CAN_FLY = 119, // 0/1
+ SMART_ACTION_REMOVE_AURAS_BY_TYPE = 120, // type
+ SMART_ACTION_SET_SIGHT_DIST = 121, // sightDistance
+ SMART_ACTION_FLEE = 122, // fleeTime
+ SMART_ACTION_ADD_THREAT = 123, // +threat, -threat
+ SMART_ACTION_LOAD_EQUIPMENT = 124, // id
+ SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT = 125, // id min range, id max range
+ SMART_ACTION_REMOVE_ALL_GAMEOBJECTS = 126,
+ SMART_ACTION_STOP_MOTION = 127, // stopMoving, movementExpired
SMART_ACTION_PLAY_ANIMKIT = 128, // id, type (0 = oneShot, 1 = aiAnim, 2 = meleeAnim, 3 = movementAnim)
SMART_ACTION_SCENE_PLAY = 129, // sceneId
SMART_ACTION_SCENE_CANCEL = 130, // sceneId
@@ -645,6 +669,7 @@ struct SmartAction
uint32 spell;
uint32 castFlags;
uint32 triggerFlags;
+ uint32 targetsLimit;
} cast;
struct
@@ -878,6 +903,11 @@ struct SmartAction
struct
{
+ uint32 disable;
+ } setDisableGravity;
+
+ struct
+ {
uint32 fly;
} setFly;
@@ -993,6 +1023,11 @@ struct SmartAction
struct
{
uint32 withEmote;
+ } fleeAssist;
+
+ struct
+ {
+ uint32 fleeTime;
} flee;
struct
@@ -1100,6 +1135,34 @@ struct SmartAction
struct
{
+ uint32 type;
+ } auraType;
+
+ struct
+ {
+ uint32 dist;
+ } sightDistance;
+
+ struct
+ {
+ uint32 id;
+ uint32 force;
+ } loadEquipment;
+
+ struct
+ {
+ uint32 minId;
+ uint32 maxId;
+ } randomTimedEvent;
+
+ struct
+ {
+ uint32 stopMovement;
+ uint32 movementExpired;
+ } stopMotion;
+
+ struct
+ {
uint32 animKit;
uint32 type;
} animKit;
@@ -1140,10 +1203,10 @@ enum SMARTAI_TARGETS
SMART_TARGET_NONE = 0, // NONE, defaulting to invoket
SMART_TARGET_SELF = 1, // Self cast
SMART_TARGET_VICTIM = 2, // Our current target (ie: highest aggro)
- SMART_TARGET_HOSTILE_SECOND_AGGRO = 3, // Second highest aggro
- SMART_TARGET_HOSTILE_LAST_AGGRO = 4, // Dead last on aggro
- SMART_TARGET_HOSTILE_RANDOM = 5, // Just any random target on our threat list
- SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 6, // Any random target except top threat
+ SMART_TARGET_HOSTILE_SECOND_AGGRO = 3, // Second highest aggro, maxdist, playerOnly, powerType + 1
+ SMART_TARGET_HOSTILE_LAST_AGGRO = 4, // Dead last on aggro, maxdist, playerOnly, powerType + 1
+ SMART_TARGET_HOSTILE_RANDOM = 5, // Just any random target on our threat list, maxdist, playerOnly, powerType + 1
+ SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 6, // Any random target except top threat, maxdist, playerOnly, powerType + 1
SMART_TARGET_ACTION_INVOKER = 7, // Unit who caused this Event to occur
SMART_TARGET_POSITION = 8, // use xyz from event params
SMART_TARGET_CREATURE_RANGE = 9, // CreatureEntry(0any), minDist, maxDist
@@ -1160,8 +1223,8 @@ enum SMARTAI_TARGETS
SMART_TARGET_CLOSEST_GAMEOBJECT = 20, // entry(0any), maxDist
SMART_TARGET_CLOSEST_PLAYER = 21, // maxDist
SMART_TARGET_ACTION_INVOKER_VEHICLE = 22, // Unit's vehicle who caused this Event to occur
- SMART_TARGET_OWNER_OR_SUMMONER = 23, // Unit's owner or summoner
- SMART_TARGET_THREAT_LIST = 24, // All units on creature's threat list
+ SMART_TARGET_OWNER_OR_SUMMONER = 23, // Unit's owner or summoner, Use Owner/Charmer of this unit
+ SMART_TARGET_THREAT_LIST = 24, // All units on creature's threat list, maxdist
SMART_TARGET_CLOSEST_ENEMY = 25, // maxDist, playerOnly
SMART_TARGET_CLOSEST_FRIENDLY = 26, // maxDist, playerOnly
SMART_TARGET_LOOT_RECIPIENTS = 27, // all players that have tagged this creature (for kill credit)
@@ -1190,6 +1253,20 @@ struct SmartTarget
{
struct
{
+ uint32 maxDist;
+ uint32 playerOnly;
+ uint32 powerType;
+ } hostilRandom;
+
+ struct
+ {
+ uint32 maxDist;
+ uint32 playerOnly;
+ uint32 isInLos;
+ } farthest;
+
+ struct
+ {
uint32 creature;
uint32 minDist;
uint32 maxDist;
@@ -1268,6 +1345,11 @@ struct SmartTarget
struct
{
+ uint32 useCharmerOrOwner;
+ } owner;
+
+ struct
+ {
uint32 param1;
uint32 param2;
uint32 param3;
@@ -1521,6 +1603,7 @@ class TC_GAME_API SmartWaypointMgr
// all events for a single entry
typedef std::vector<SmartScriptHolder> SmartAIEventList;
+typedef std::vector<SmartScriptHolder> SmartAIEventStoredList;
// all events for all entries / guids
typedef std::unordered_map<int64, SmartAIEventList> SmartAIEventMap;
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 66186cdeeab..8f63d58da85 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -2163,17 +2163,6 @@ Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const
return target;
}
-Player* Creature::SelectNearestPlayer(float distance) const
-{
- Player* target = nullptr;
-
- Trinity::NearestPlayerInObjectRangeCheck checker(this, distance);
- Trinity::PlayerLastSearcher<Trinity::NearestPlayerInObjectRangeCheck> searcher(this, target, checker);
- Cell::VisitAllObjects(this, searcher, distance);
-
- return target;
-}
-
void Creature::SendAIReaction(AiReaction reactionType)
{
WorldPackets::Combat::AIReaction packet;
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index c92d664984f..cfc8aa3ddc2 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -224,7 +224,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
Unit* SelectNearestTarget(float dist = 0, bool playerOnly = false) const;
Unit* SelectNearestTargetInAttackDistance(float dist = 0) const;
- Player* SelectNearestPlayer(float distance = 0) const;
Unit* SelectNearestHostileUnitInAggroRange(bool useLOS = false) const;
void DoFleeToGetAssistance();
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 944a1c0922d..bda1e0bae21 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -615,6 +615,10 @@ void GameObject::Update(uint32 diff)
return;
}
+ // Call AI Reset (required for example in SmartAI to clear one time events)
+ if (AI())
+ AI()->Reset();
+
// Respawn timer
uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0;
if (poolid)
@@ -1353,7 +1357,7 @@ void GameObject::Use(Unit* user)
if (sScriptMgr->OnGossipHello(playerUser, this))
return;
- if (AI()->GossipHello(playerUser, true))
+ if (AI()->GossipHello(playerUser, false))
return;
}
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index b7894cef6b0..b6c7bb03166 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1885,7 +1885,7 @@ Creature* WorldObject::SummonTrigger(float x, float y, float z, float ang, uint3
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Creature* summon = SummonCreature(WORLD_TRIGGER, x, y, z, ang, summonType, duration);
if (!summon)
- return NULL;
+ return nullptr;
//summon->SetName(GetName());
if (GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT)
@@ -1924,7 +1924,7 @@ void WorldObject::SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list
Creature* WorldObject::FindNearestCreature(uint32 entry, float range, bool alive) const
{
- Creature* creature = NULL;
+ Creature* creature = nullptr;
Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck checker(*this, entry, alive, range);
Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(this, creature, checker);
Cell::VisitAllObjects(this, searcher, range);
@@ -1933,7 +1933,7 @@ Creature* WorldObject::FindNearestCreature(uint32 entry, float range, bool alive
GameObject* WorldObject::FindNearestGameObject(uint32 entry, float range) const
{
- GameObject* go = NULL;
+ GameObject* go = nullptr;
Trinity::NearestGameObjectEntryInObjectRangeCheck checker(*this, entry, range);
Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> searcher(this, go, checker);
Cell::VisitGridObjects(this, searcher, range);
@@ -1942,13 +1942,24 @@ GameObject* WorldObject::FindNearestGameObject(uint32 entry, float range) const
GameObject* WorldObject::FindNearestGameObjectOfType(GameobjectTypes type, float range) const
{
- GameObject* go = NULL;
+ GameObject* go = nullptr;
Trinity::NearestGameObjectTypeInObjectRangeCheck checker(*this, type, range);
Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectTypeInObjectRangeCheck> searcher(this, go, checker);
Cell::VisitGridObjects(this, searcher, range);
return go;
}
+Player* WorldObject::SelectNearestPlayer(float distance) const
+{
+ Player* target = nullptr;
+
+ Trinity::NearestPlayerInObjectRangeCheck checker(this, distance);
+ Trinity::PlayerLastSearcher<Trinity::NearestPlayerInObjectRangeCheck> searcher(this, target, checker);
+ Cell::VisitGridObjects(this, searcher, distance);
+
+ return target;
+}
+
template <typename Container>
void WorldObject::GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange /*= 250.0f*/) const
{
@@ -2021,7 +2032,7 @@ void WorldObject::GetNearPoint(WorldObject const* /*searcher*/, float &x, float
void WorldObject::GetClosePoint(float &x, float &y, float &z, float size, float distance2d /*= 0*/, float angle /*= 0*/) const
{
// angle calculated from current orientation
- GetNearPoint(NULL, x, y, z, size, distance2d, GetOrientation() + angle);
+ GetNearPoint(nullptr, x, y, z, size, distance2d, GetOrientation() + angle);
}
Position WorldObject::GetNearPosition(float dist, float angle)
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 84157b8f58b..d5fb9f3c73f 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -491,6 +491,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
Creature* FindNearestCreature(uint32 entry, float range, bool alive = true) const;
GameObject* FindNearestGameObject(uint32 entry, float range) const;
GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const;
+ Player* SelectNearestPlayer(float distance) const;
template <typename Container>
void GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange = 250.0f) const;
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index ea734749343..9ebcebc8f0b 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -261,7 +261,7 @@ void WorldSession::HandleGameobjectReportUse(WorldPackets::GameObject::GameObjRe
if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(packet.Guid))
{
- if (go->AI()->GossipHello(_player, false))
+ if (go->AI()->GossipHello(_player, true))
return;
_player->UpdateCriteria(CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 67cafa53df6..8e7dcfa4fbd 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -28,6 +28,7 @@
#include "DatabaseEnv.h"
#include "DisableMgr.h"
#include "DynamicObject.h"
+#include "GameObjectAI.h"
#include "GridNotifiersImpl.h"
#include "Guild.h"
#include "InstanceScript.h"
@@ -2747,6 +2748,9 @@ void Spell::DoAllEffectOnTarget(GOTargetInfo* target)
if (effect && (effectMask & (1 << effect->EffectIndex)))
HandleEffects(NULL, NULL, go, effect->EffectIndex, SPELL_EFFECT_HANDLE_HIT_TARGET);
+ if (go->AI())
+ go->AI()->SpellHit(m_caster, m_spellInfo);
+
CallScriptOnHitHandlers();
CallScriptAfterHitHandlers();
}
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index c50637620cc..60a166f4a19 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -1633,7 +1633,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype)
if (sScriptMgr->OnGossipHello(player, gameObjTarget))
return;
- if (gameObjTarget->AI()->GossipHello(player, true))
+ if (gameObjTarget->AI()->GossipHello(player, false))
return;
switch (gameObjTarget->GetGoType())
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index 9ee81f17b30..782cd1b9916 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -1373,10 +1373,8 @@ class go_twilight_portal : public GameObjectScript
}
}
- bool GossipHello(Player* player, bool isUse) override
+ bool GossipHello(Player* player, bool /*reportUse*/) override
{
- if (!isUse)
- return true;
if (_spellId != 0)
player->CastSpell(player, _spellId, true);
return true;
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
index 186a3ca51f1..0bb4b090a01 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
@@ -987,11 +987,8 @@ class go_celestial_planetarium_access : public GameObjectScript
{
}
- bool GossipHello(Player* player, bool isUse) override
+ bool GossipHello(Player* player, bool /*reportUse*/) override
{
- if (!isUse)
- return true;
-
if (go->HasFlag(GO_FLAG_IN_USE))
return true;
diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp
index a1d8a7f72dd..e03e23aeb90 100644
--- a/src/server/scripts/World/go_scripts.cpp
+++ b/src/server/scripts/World/go_scripts.cpp
@@ -840,11 +840,8 @@ class go_soulwell : public GameObjectScript
{
}
- bool GossipHello(Player* player, bool isUse) override
+ bool GossipHello(Player* player, bool /*reportUse*/) override
{
- if (!isUse)
- return true;
-
Unit* owner = go->GetOwner();
if (!owner || owner->GetTypeId() != TYPEID_PLAYER || !player->IsInSameRaidWith(owner->ToPlayer()))
return true;