diff options
author | xinef1 <w.szyszko2@gmail.com> | 2017-02-17 21:33:18 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2019-08-17 20:04:14 +0200 |
commit | a32d5cfa1762ae1158ee2f40b4c9b36e5b41913a (patch) | |
tree | d55a3961fc9520ae7c9fa619911ea8a892559617 /src | |
parent | 2caec4f4d20b4c0f91abbcc60b756e00838c7bdd (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')
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; |