From 7fff83d67526efff63867d41b9e036a19a9287b3 Mon Sep 17 00:00:00 2001 From: ccrs Date: Sat, 12 Aug 2017 01:40:25 +0200 Subject: Core/Movement: waypoint movement (#20121) Following the work done in #19361 this is the cleanup and improvement of the related logic of waypoint management. Ref 28050f3 #18020 (taking the good parts and ignoring the incomplete work) --- src/server/game/AI/SmartScripts/SmartAI.cpp | 399 +++++++++------------ src/server/game/AI/SmartScripts/SmartAI.h | 70 ++-- src/server/game/AI/SmartScripts/SmartScript.cpp | 29 +- src/server/game/AI/SmartScripts/SmartScriptMgr.cpp | 59 ++- src/server/game/AI/SmartScripts/SmartScriptMgr.h | 39 +- 5 files changed, 260 insertions(+), 336 deletions(-) (limited to 'src/server/game/AI/SmartScripts') diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 71cb6f7fdad..58f8f72d47b 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -17,6 +17,7 @@ #include "SmartAI.h" #include "Creature.h" +#include "CreatureGroups.h" #include "DBCStructure.h" #include "GameObject.h" #include "Group.h" @@ -28,54 +29,12 @@ #include "ScriptMgr.h" #include "Vehicle.h" -SmartAI::SmartAI(Creature* c) : CreatureAI(c) +SmartAI::SmartAI(Creature* creature) : CreatureAI(creature), mIsCharmed(false), mFollowCreditType(0), mFollowArrivedTimer(0), mFollowCredit(0), mFollowArrivedEntry(0), mFollowDist(0.f), mFollowAngle(0.f), + _escortState(SMART_ESCORT_NONE), _escortNPCFlags(0), _escortInvokerCheckTimer(1000), _currentWaypointNode(0), _waypointReached(false), _waypointPauseTimer(0), _waypointPauseForced(false), _repeatWaypointPath(false), + _OOCReached(false), _waypointPathEnded(false), mRun(true), mEvadeDisabled(false), mCanAutoAttack(true), mCanCombatMove(true), mInvincibilityHpLevel(0), mDespawnTime(0), mDespawnState(0), mJustReset(false), + mConditionsTimer(0), _gossipReturn(false) { - mIsCharmed = false; - // copy script to local (protection for table reload) - - mWayPoints = nullptr; - mEscortState = SMART_ESCORT_NONE; - mCurrentWPID = 0;//first wp id is 1 !! - mWPReached = false; - mWPPauseTimer = 0; - mEscortNPCFlags = 0; - mLastWP = nullptr; - - mCanRepeatPath = false; - - // Spawn in run mode - me->SetWalk(false); - mRun = false; - mEvadeDisabled = false; - - mLastOOCPos = me->GetPosition(); - - mCanAutoAttack = true; - mCanCombatMove = true; - - mForcedPaused = false; - mLastWPIDReached = 0; - - mEscortQuestID = 0; - - mDespawnTime = 0; - mDespawnState = 0; - - mEscortInvokerCheckTimer = 1000; - mFollowGuid.Clear(); - mFollowDist = 0; - mFollowAngle = 0; - mFollowCredit = 0; - mFollowArrivedEntry = 0; - mFollowCreditType = 0; - mFollowArrivedTimer = 0; - mInvincibilityHpLevel = 0; - - mJustReset = false; - mConditionsTimer = 0; - mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, c->GetEntry()); - - _gossipReturn = false; + mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, creature->GetEntry()); } bool SmartAI::IsAIControlled() const @@ -101,74 +60,68 @@ void SmartAI::UpdateDespawn(uint32 diff) } else mDespawnTime -= diff; } -WayPoint* SmartAI::GetNextWayPoint() +void SmartAI::StartPath(bool run/* = false*/, uint32 pathId/* = 0*/, bool repeat/* = false*/, Unit* invoker/* = nullptr*/, uint32 nodeId/* = 1*/) { - if (!mWayPoints || mWayPoints->empty()) - return nullptr; - - mCurrentWPID++; - WPPath::const_iterator itr = mWayPoints->find(mCurrentWPID); - if (itr != mWayPoints->end()) + if (me->IsInCombat()) // no wp movement in combat { - mLastWP = (*itr).second; - if (mLastWP->id != mCurrentWPID) - { - TC_LOG_ERROR("misc", "SmartAI::GetNextWayPoint: Got not expected waypoint id %u, expected %u", mLastWP->id, mCurrentWPID); - } - return (*itr).second; - } - return nullptr; -} - -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()); + TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement (%u) while in combat, ignoring.", me->GetEntry(), pathId); return; } if (HasEscortState(SMART_ESCORT_ESCORTING)) StopPath(); - if (path) + SetRun(run); + + if (pathId) { - if (!LoadPath(path)) + if (!LoadPath(pathId)) return; } - if (!mWayPoints || mWayPoints->empty()) + if (_path.nodes.empty()) return; - if (WayPoint* wp = GetNextWayPoint()) - { - AddEscortState(SMART_ESCORT_ESCORTING); - mCanRepeatPath = repeat; + _currentWaypointNode = nodeId; + _waypointPathEnded = false; - SetRun(run); + _repeatWaypointPath = repeat; - if (invoker && invoker->GetTypeId() == TYPEID_PLAYER) - { - mEscortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS); - me->SetUInt32Value(UNIT_NPC_FLAGS, 0); - } + // Do not use AddEscortState, removing everything from previous + _escortState = SMART_ESCORT_ESCORTING; - 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()); + if (invoker && invoker->GetTypeId() == TYPEID_PLAYER) + { + _escortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS); + me->SetFlag(UNIT_NPC_FLAGS, 0); } + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, _currentWaypointNode, GetScript()->GetPathId()); + + me->GetMotionMaster()->MovePath(_path, _repeatWaypointPath); } bool SmartAI::LoadPath(uint32 entry) { if (HasEscortState(SMART_ESCORT_ESCORTING)) return false; - mWayPoints = sSmartWaypointMgr->GetPath(entry); - if (!mWayPoints) + + WaypointPath const* path = sSmartWaypointMgr->GetPath(entry); + if (!path || path->nodes.empty()) { GetScript()->SetPathId(0); return false; } + + _path.id = path->id; + _path.nodes = path->nodes; + for (WaypointNode& waypoint : _path.nodes) + { + Trinity::NormalizeMapCoord(waypoint.x); + Trinity::NormalizeMapCoord(waypoint.y); + waypoint.moveType = mRun ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK; + } + GetScript()->SetPathId(entry); return true; } @@ -177,22 +130,26 @@ void SmartAI::PausePath(uint32 delay, bool forced) { if (!HasEscortState(SMART_ESCORT_ESCORTING)) return; + if (HasEscortState(SMART_ESCORT_PAUSED)) { - TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint movement while already paused, ignoring.", me->GetEntry()); + TC_LOG_ERROR("misc", "SmartAI::PausePath: Creature entry %u wanted to pause waypoint (current waypoint: %u) movement while already paused, ignoring.", me->GetEntry(), _currentWaypointNode); return; } - mForcedPaused = forced; - mLastOOCPos = me->GetPosition(); - AddEscortState(SMART_ESCORT_PAUSED); - mWPPauseTimer = delay; + + _waypointPauseTimer = delay; + if (forced) { + _waypointPauseForced = forced; SetRun(mRun); - me->StopMoving();//force stop - me->GetMotionMaster()->MoveIdle();//force stop + me->PauseMovement(); } - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, mLastWP->id, GetScript()->GetPathId()); + else + _waypointReached = false; + + AddEscortState(SMART_ESCORT_PAUSED); + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_PAUSED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); } void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail) @@ -202,40 +159,29 @@ void SmartAI::StopPath(uint32 DespawnTime, uint32 quest, bool fail) if (quest) mEscortQuestID = quest; - SetDespawnTime(DespawnTime); - //mDespawnTime = DespawnTime; - mLastOOCPos = me->GetPosition(); - me->StopMoving();//force stop + if (mDespawnState != 2) + SetDespawnTime(DespawnTime); + me->GetMotionMaster()->MoveIdle(); - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, mLastWP->id, GetScript()->GetPathId()); + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_STOPPED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); + EndPath(fail); } void SmartAI::EndPath(bool fail) { - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, mLastWP->id, GetScript()->GetPathId()); - RemoveEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING); - mWayPoints = nullptr; - mCurrentWPID = 0; - mWPPauseTimer = 0; - mLastWP = nullptr; + _path.nodes.clear(); + _waypointPauseTimer = 0; - if (mEscortNPCFlags) + if (_escortNPCFlags) { - me->SetUInt32Value(UNIT_NPC_FLAGS, mEscortNPCFlags); - mEscortNPCFlags = 0; + me->SetFlag(UNIT_NPC_FLAGS, _escortNPCFlags); + _escortNPCFlags = 0; } - if (mCanRepeatPath) - { - if (IsAIControlled()) - StartPath(mRun, GetScript()->GetPathId(), true); - } - else - GetScript()->SetPathId(0); - ObjectVector const* targets = GetScript()->GetStoredTargetVector(SMART_ESCORT_TARGETS, *me); if (targets && mEscortQuestID) { @@ -279,15 +225,36 @@ void SmartAI::EndPath(bool fail) } } + // End Path events should be only processed if it was SUCCESSFUL stop or stop called by SMART_ACTION_WAYPOINT_STOP + if (fail) + return; + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_ENDED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); + + if (_repeatWaypointPath) + { + if (IsAIControlled()) + StartPath(mRun, GetScript()->GetPathId(), _repeatWaypointPath); + } + else + GetScript()->SetPathId(0); + if (mDespawnState == 1) StartDespawn(); } void SmartAI::ResumePath() { + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, nullptr, _currentWaypointNode, GetScript()->GetPathId()); + + RemoveEscortState(SMART_ESCORT_PAUSED); + + _waypointPauseForced = false; + _waypointReached = false; + _waypointPauseTimer = 0; + SetRun(mRun); - if (mLastWP) - me->GetMotionMaster()->MovePoint(mLastWP->id, mLastWP->x, mLastWP->y, mLastWP->z); + me->ResumeMovement(); } void SmartAI::ReturnToLastOOCPos() @@ -295,82 +262,57 @@ void SmartAI::ReturnToLastOOCPos() if (!IsAIControlled()) return; - SetRun(mRun); - me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, mLastOOCPos); + me->SetWalk(false); + me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, me->GetHomePosition()); } void SmartAI::UpdatePath(const uint32 diff) { if (!HasEscortState(SMART_ESCORT_ESCORTING)) return; - if (mEscortInvokerCheckTimer < diff) + + if (_escortInvokerCheckTimer < diff) { - // Escort failed, no players in range if (!IsEscortInvokerInRange()) { 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); + me->DespawnOrUnsummon(); return; } - mEscortInvokerCheckTimer = 1000; + _escortInvokerCheckTimer = 1000; } else - mEscortInvokerCheckTimer -= diff; + _escortInvokerCheckTimer -= diff; // handle pause - if (HasEscortState(SMART_ESCORT_PAUSED)) + if (HasEscortState(SMART_ESCORT_PAUSED) && (_waypointReached || _waypointPauseForced)) { - if (mWPPauseTimer < diff) + if (_waypointPauseTimer <= diff) { - if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING) && (mWPReached || mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT || mForcedPaused)) - { - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_RESUMED, nullptr, mLastWP->id, GetScript()->GetPathId()); - RemoveEscortState(SMART_ESCORT_PAUSED); - if (mForcedPaused)// if paused between 2 wps resend movement - { - ResumePath(); - mWPReached = false; - mForcedPaused = false; - } - if (mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT) - mWPReached = true; - } - - mWPPauseTimer = 0; + if (!me->IsInCombat() && !HasEscortState(SMART_ESCORT_RETURNING)) + ResumePath(); } else - mWPPauseTimer -= diff; + _waypointPauseTimer -= diff; + } + else if (_waypointPathEnded) // end path + { + _waypointPathEnded = false; + StopPath(); + return; } if (HasEscortState(SMART_ESCORT_RETURNING)) { - if (mWPReached)//reached OOC WP + if (_OOCReached) // reached OOC WP { + _OOCReached = false; RemoveEscortState(SMART_ESCORT_RETURNING); if (!HasEscortState(SMART_ESCORT_PAUSED)) ResumePath(); - mWPReached = false; - } - } - - if ((!me->HasReactState(REACT_PASSIVE) && me->IsInCombat()) || HasEscortState(SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING)) - return; - - // handle next wp - if (mWPReached)//reached WP - { - mWPReached = false; - if (mCurrentWPID == GetWPCount()) - { - EndPath(); - } - else if (WayPoint* wp = GetNextWayPoint()) - { - SetRun(mRun); - me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z); } } } @@ -450,24 +392,43 @@ bool SmartAI::IsEscortInvokerInRange() return true; } -void SmartAI::MovepointReached(uint32 id) +///@todo Implement new smart event SMART_EVENT_WAYPOINT_STARTED +void SmartAI::WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { - if (id != SMART_ESCORT_LAST_OOC_POINT && mLastWPIDReached != id) - GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, id); +} + +void SmartAI::WaypointReached(uint32 nodeId, uint32 pathId) +{ + _currentWaypointNode = nodeId; + + GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_REACHED, nullptr, _currentWaypointNode, pathId); - mLastWPIDReached = id; - mWPReached = true; + if (_waypointPauseTimer && !_waypointPauseForced) + { + _waypointReached = true; + me->PauseMovement(); + } + else if (HasEscortState(SMART_ESCORT_ESCORTING) && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) + { + if (_currentWaypointNode == _path.nodes.size()) + _waypointPathEnded = true; + else + SetRun(mRun); + } } -void SmartAI::MovementInform(uint32 MovementType, uint32 Data) +void SmartAI::MovementInform(uint32 type, uint32 id) { - if ((MovementType == POINT_MOTION_TYPE && Data == SMART_ESCORT_LAST_OOC_POINT) || MovementType == FOLLOW_MOTION_TYPE) + if (type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT) me->ClearUnitState(UNIT_STATE_EVADE); - GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, MovementType, Data); - if (MovementType != POINT_MOTION_TYPE || !HasEscortState(SMART_ESCORT_ESCORTING)) + GetScript()->ProcessEventsFor(SMART_EVENT_MOVEMENTINFORM, nullptr, type, id); + + if (!HasEscortState(SMART_ESCORT_ESCORTING)) return; - MovepointReached(Data); + + if (type == POINT_MOTION_TYPE && id == SMART_ESCORT_LAST_OOC_POINT) + _OOCReached = true; } void SmartAI::EnterEvadeMode(EvadeReason /*why*/) @@ -511,8 +472,8 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/) else me->GetMotionMaster()->MoveTargetedHome(); - if (!HasEscortState(SMART_ESCORT_ESCORTING)) //dont mess up escort movement after combat - SetRun(mRun); + if (!me->HasUnitState(UNIT_STATE_EVADE)) + GetScript()->OnReset(); } void SmartAI::MoveInLineOfSight(Unit* who) @@ -544,19 +505,19 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who) if (!who || !who->GetVictim()) return false; - //experimental (unknown) flag not present + // experimental (unknown) flag not present if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; - //not a player + // not a player if (!who->EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()) return false; - //never attack friendly + // never attack friendly if (!me->IsValidAssistTarget(who->GetVictim())) return false; - //too far away and no free sight? + // too far away and no free sight? if (me->IsWithinDistInMap(who, SMART_MAX_AID_DIST) && me->IsWithinLOSInMap(who)) { me->EngageWithTarget(who); @@ -570,14 +531,14 @@ void SmartAI::JustAppeared() { mDespawnTime = 0; mDespawnState = 0; - mEscortState = SMART_ESCORT_NONE; + _escortState = SMART_ESCORT_NONE; me->SetVisible(true); if (me->GetFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); mJustReset = true; JustReachedHome(); GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN); - mFollowGuid.Clear();//do not reset follower on Reset(), we need it after combat evade + mFollowGuid.Clear(); // do not reset follower on Reset(), we need it after combat evade mFollowDist = 0; mFollowAngle = 0; mFollowCredit = 0; @@ -594,8 +555,16 @@ void SmartAI::JustReachedHome() { GetScript()->ProcessEventsFor(SMART_EVENT_REACHED_HOME); - if (!UpdateVictim() && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE && me->GetWaypointPath()) - me->GetMotionMaster()->MovePath(me->GetWaypointPath(), true); + CreatureGroup* formation = me->GetFormation(); + if (!formation || formation->getLeader() == me || !formation->isFormed()) + { + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE && me->GetWaypointPath()) + me->GetMotionMaster()->MovePath(me->GetWaypointPath(), true); + else + me->ResumeMovement(); + } + else if (formation->isFormed()) + me->GetMotionMaster()->MoveIdle(); // wait the order of leader } mJustReset = false; @@ -607,24 +576,14 @@ void SmartAI::EnterCombat(Unit* enemy) me->InterruptNonMeleeSpells(false); // must be before ProcessEvents GetScript()->ProcessEventsFor(SMART_EVENT_AGGRO, enemy); - - if (!IsAIControlled()) - return; - mLastOOCPos = me->GetPosition(); - SetRun(mRun); - if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == POINT_MOTION_TYPE) - me->GetMotionMaster()->MovementExpired(); } void SmartAI::JustDied(Unit* killer) { - GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer); if (HasEscortState(SMART_ESCORT_ESCORTING)) - { EndPath(true); - me->StopMoving();//force stop - me->GetMotionMaster()->MoveIdle(); - } + + GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, killer); } void SmartAI::KilledUnit(Unit* victim) @@ -642,15 +601,21 @@ void SmartAI::AttackStart(Unit* who) // dont allow charmed npcs to act on their own if (!IsAIControlled()) { - if (who && mCanAutoAttack) - me->Attack(who, true); + if (who) + me->Attack(who, mCanAutoAttack); return; } - if (who && me->Attack(who, me->IsWithinMeleeRange(who))) + if (who && me->Attack(who, mCanAutoAttack)) { + me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); + me->PauseMovement(); + if (mCanCombatMove) + { + SetRun(mRun); me->GetMotionMaster()->MoveChase(who); + } } } @@ -713,10 +678,10 @@ void SmartAI::PassengerBoarded(Unit* who, int8 seatId, bool apply) void SmartAI::InitializeAI() { GetScript()->OnInitialize(me); + if (!me->isDead()) { - mJustReset = true; - JustReachedHome(); + GetScript()->OnReset(); GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN); } } @@ -727,13 +692,13 @@ void SmartAI::OnCharmed(bool apply) { if (HasEscortState(SMART_ESCORT_ESCORTING | SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING)) EndPath(true); - me->StopMoving(); } + mIsCharmed = apply; if (!apply && !me->IsInEvadeMode()) { - if (mCanRepeatPath) + if (_repeatWaypointPath) StartPath(mRun, GetScript()->GetPathId(), true); else me->SetWalk(!mRun); @@ -826,30 +791,21 @@ void SmartAI::SetCombatMove(bool on) { if (mCanCombatMove == on) return; + mCanCombatMove = on; + if (!IsAIControlled()) return; - if (!HasEscortState(SMART_ESCORT_ESCORTING)) + + if (me->IsEngaged()) { - if (on && me->GetVictim()) + if (on && !me->HasReactState(REACT_PASSIVE) && me->GetVictim() && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == MAX_MOTION_TYPE) { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) - { - SetRun(mRun); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - me->CastStop(); - } - } - else - { - if (me->HasUnitState(UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE)) - return; - - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->Clear(true); - me->StopMoving(); - me->GetMotionMaster()->MoveIdle(); + SetRun(mRun); + me->GetMotionMaster()->MoveChase(me->GetVictim()); } + else if (!on && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == CHASE_MOTION_TYPE) + me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); } } @@ -881,7 +837,6 @@ void SmartAI::StopFollow(bool complete) mFollowArrivedTimer = 1000; mFollowArrivedEntry = 0; mFollowCreditType = 0; - me->StopMoving(); me->GetMotionMaster()->MoveIdle(); if (!complete) diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 3834fd01d07..9c22b509bc9 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -23,8 +23,7 @@ #include "GameObjectAI.h" #include "Position.h" #include "SmartScript.h" - -struct WayPoint; +#include "WaypointDefines.h" enum SmartEscortState { @@ -43,32 +42,37 @@ enum SmartEscortVars class TC_GAME_API SmartAI : public CreatureAI { public: - ~SmartAI(){ } + ~SmartAI() { } explicit SmartAI(Creature* c); + // core related + static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; } + // Check whether we are currently permitted to make the creature take action bool IsAIControlled() const; // Start moving to the desired MovePoint - void StartPath(bool run = false, uint32 path = 0, bool repeat = false, Unit* invoker = nullptr); + void StartPath(bool run = false, uint32 pathId = 0, bool repeat = false, Unit* invoker = nullptr, uint32 nodeId = 1); bool LoadPath(uint32 entry); void PausePath(uint32 delay, bool forced = false); void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false); void EndPath(bool fail = false); void ResumePath(); - WayPoint* GetNextWayPoint(); - bool HasEscortState(uint32 uiEscortState) const { return (mEscortState & uiEscortState) != 0; } - void AddEscortState(uint32 uiEscortState) { mEscortState |= uiEscortState; } - void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; } + bool HasEscortState(uint32 uiEscortState) const { return (_escortState & uiEscortState) != 0; } + void AddEscortState(uint32 uiEscortState) { _escortState |= uiEscortState; } + void RemoveEscortState(uint32 uiEscortState) { _escortState &= ~uiEscortState; } void SetAutoAttack(bool on) { mCanAutoAttack = on; } 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(bool complete); + bool IsEscortInvokerInRange(); + + void WaypointStarted(uint32 nodeId, uint32 pathId) override; + void WaypointReached(uint32 nodeId, uint32 pathId) override; void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker); SmartScript* GetScript() { return &mScript; } - bool IsEscortInvokerInRange(); // Called when creature is spawned or respawned void JustAppeared() override; @@ -157,12 +161,6 @@ class TC_GAME_API SmartAI : public CreatureAI // Used in scripts to share variables ObjectGuid GetGUID(int32 id = 0) const override; - //core related - static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; } - - // Called at movepoint reached - void MovepointReached(uint32 id); - // Makes the creature run/walk void SetRun(bool run = true); @@ -194,11 +192,20 @@ class TC_GAME_API SmartAI : public CreatureAI void OnSpellClick(Unit* clicker, bool& result) override; - void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; } + void SetWPPauseTimer(uint32 time) { _waypointPauseTimer = time; } void SetGossipReturn(bool val) { _gossipReturn = val; } private: + bool AssistPlayerInCombatAgainst(Unit* who); + void ReturnToLastOOCPos(); + void UpdatePath(const uint32 diff); + void UpdateDespawn(uint32 diff); + // Vehicle conditions + void CheckConditions(uint32 diff); + + SmartScript mScript; + bool mIsCharmed; uint32 mFollowCreditType; uint32 mFollowArrivedTimer; @@ -208,36 +215,29 @@ class TC_GAME_API SmartAI : public CreatureAI float mFollowDist; float mFollowAngle; - void ReturnToLastOOCPos(); - void UpdatePath(const uint32 diff); - SmartScript mScript; - WPPath* mWayPoints; - uint32 mEscortState; - uint32 mCurrentWPID; - 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; } - bool mCanRepeatPath; + uint32 _escortState; + uint32 _escortNPCFlags; + uint32 _escortInvokerCheckTimer; + WaypointPath _path; + uint32 _currentWaypointNode; + bool _waypointReached; + uint32 _waypointPauseTimer; + bool _waypointPauseForced; + bool _repeatWaypointPath; + bool _OOCReached; + bool _waypointPathEnded; + bool mRun; bool mEvadeDisabled; bool mCanAutoAttack; bool mCanCombatMove; - bool mForcedPaused; uint32 mInvincibilityHpLevel; - bool AssistPlayerInCombatAgainst(Unit* who); uint32 mDespawnTime; uint32 mDespawnState; - void UpdateDespawn(uint32 diff); - uint32 mEscortInvokerCheckTimer; bool mJustReset; // Vehicle conditions - void CheckConditions(uint32 diff); bool mHasConditions; uint32 mConditionsTimer; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 79f3b7ce652..7f002ed6671 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -39,6 +39,7 @@ #include "SpellMgr.h" #include "TemporarySummon.h" #include "Vehicle.h" +#include "WaypointDefines.h" #include SmartScript::SmartScript() @@ -1990,7 +1991,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u std::back_inserter(waypoints), [](uint32 wp) { return wp != 0; }); float distanceToClosest = std::numeric_limits::max(); - WayPoint* closestWp = nullptr; + std::pair closest = { 0, 0 }; for (WorldObject* target : targets) { @@ -1998,29 +1999,27 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (IsSmart(creature)) { - for (uint32 wp : waypoints) + for (uint32 pathId : waypoints) { - WPPath* path = sSmartWaypointMgr->GetPath(wp); - if (!path || path->empty()) + WaypointPath const* path = sSmartWaypointMgr->GetPath(pathId); + if (!path || path->nodes.empty()) continue; - auto itrWp = path->find(0); - if (itrWp != path->end()) + for (auto itr = path->nodes.begin(); itr != path->nodes.end(); ++itr) { - if (WayPoint* wp = itrWp->second) + WaypointNode const waypoint = *itr; + float distamceToThisNode = creature->GetDistance(waypoint.x, waypoint.y, waypoint.z); + if (distamceToThisNode < distanceToClosest) { - float distToThisPath = creature->GetDistance(wp->x, wp->y, wp->z); - if (distToThisPath < distanceToClosest) - { - distanceToClosest = distToThisPath; - closestWp = wp; - } + distanceToClosest = distamceToThisNode; + closest.first = pathId; + closest.second = waypoint.id; } } } - if (closestWp) - CAST_AI(SmartAI, creature->AI())->StartPath(false, closestWp->id, true); + if (closest.first != 0) + CAST_AI(SmartAI, creature->AI())->StartPath(false, closest.first, true, nullptr, closest.second); } } } diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 3555575b2b2..b4713b7ed4d 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -29,6 +29,7 @@ #include "SpellMgr.h" #include "Timer.h" #include "UnitDefines.h" +#include "WaypointDefines.h" SmartWaypointMgr* SmartWaypointMgr::instance() { @@ -40,15 +41,7 @@ void SmartWaypointMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - for (std::unordered_map::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr) - { - for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr) - delete pathItr->second; - - delete itr->second; - } - - waypoint_map.clear(); + _waypointStore.clear(); PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -62,50 +55,47 @@ void SmartWaypointMgr::LoadFromDB() uint32 count = 0; uint32 total = 0; - uint32 last_entry = 0; - uint32 last_id = 1; + uint32 lastEntry = 0; + uint32 lastId = 1; do { Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); uint32 id = fields[1].GetUInt32(); - float x, y, z; - x = fields[2].GetFloat(); - y = fields[3].GetFloat(); - z = fields[4].GetFloat(); + float x = fields[2].GetFloat(); + float y = fields[3].GetFloat(); + float z = fields[4].GetFloat(); - if (last_entry != entry) + if (lastEntry != entry) { - waypoint_map[entry] = new WPPath(); - last_id = 1; - count++; + lastId = 1; + ++count; } - if (last_id != id) - TC_LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, last_id); + if (lastId != id) + TC_LOG_ERROR("sql.sql", "SmartWaypointMgr::LoadFromDB: Path entry %u, unexpected point id %u, expected %u.", entry, id, lastId); - last_id++; - (*waypoint_map[entry])[id] = new WayPoint(id, x, y, z); + ++lastId; + WaypointPath& path = _waypointStore[entry]; + path.id = entry; + path.nodes.emplace_back(id, x, y, z); - last_entry = entry; - total++; + lastEntry = entry; + ++total; } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u SmartAI waypoint paths (total %u waypoints) in %u ms", count, total, GetMSTimeDiffToNow(oldMSTime)); } -SmartWaypointMgr::~SmartWaypointMgr() +WaypointPath const* SmartWaypointMgr::GetPath(uint32 id) { - for (std::unordered_map::iterator itr = waypoint_map.begin(); itr != waypoint_map.end(); ++itr) - { - for (WPPath::iterator pathItr = itr->second->begin(); pathItr != itr->second->end(); ++pathItr) - delete pathItr->second; - - delete itr->second; - } + auto itr = _waypointStore.find(id); + if (itr != _waypointStore.end()) + return &itr->second; + return nullptr; } SmartAIMgr* SmartAIMgr::instance() @@ -1314,7 +1304,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) break; case SMART_ACTION_WP_START: { - if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID)) + WaypointPath const* path = sSmartWaypointMgr->GetPath(e.action.wpStart.pathID); + if (!path || path->nodes.empty()) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature %d Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID); return false; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index f2ba244aaae..747b3c3d2c4 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -20,6 +20,7 @@ #include "Define.h" #include "ObjectGuid.h" +#include "WaypointDefines.h" #include #include #include @@ -27,22 +28,6 @@ class WorldObject; enum SpellEffIndex : uint8; -struct WayPoint -{ - WayPoint(uint32 _id, float _x, float _y, float _z) - { - id = _id; - x = _x; - y = _y; - z = _z; - } - - uint32 id; - float x; - float y; - float z; -}; - enum eSmartAI { SMART_EVENT_PARAM_COUNT = 4, @@ -1514,8 +1499,6 @@ struct SmartScriptHolder operator bool() const { return entryOrGuid != 0; } }; -typedef std::unordered_map WPPath; - typedef std::vector ObjectVector; class ObjectGuidVector @@ -1542,26 +1525,22 @@ typedef std::unordered_map ObjectVectorMap; class TC_GAME_API SmartWaypointMgr { - private: - SmartWaypointMgr() { } - ~SmartWaypointMgr(); - public: static SmartWaypointMgr* instance(); void LoadFromDB(); - WPPath* GetPath(uint32 id) - { - if (waypoint_map.find(id) != waypoint_map.end()) - return waypoint_map[id]; - else return nullptr; - } + WaypointPath const* GetPath(uint32 id); private: - std::unordered_map waypoint_map; + SmartWaypointMgr() { } + ~SmartWaypointMgr() { } + + std::unordered_map _waypointStore; }; +#define sSmartWaypointMgr SmartWaypointMgr::instance() + // all events for a single entry typedef std::vector SmartAIEventList; typedef std::vector SmartAIEventStoredList; @@ -1627,5 +1606,5 @@ class TC_GAME_API SmartAIMgr }; #define sSmartScriptMgr SmartAIMgr::instance() -#define sSmartWaypointMgr SmartWaypointMgr::instance() + #endif -- cgit v1.2.3