diff options
| author | ccrs <ccrs@users.noreply.github.com> | 2017-08-12 01:40:25 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2020-08-23 00:45:46 +0200 |
| commit | 97585597f0b1aff93873fe4d757556731bc0c1b2 (patch) | |
| tree | fda9b11c6e7abb9e4d3a6108a09def640c3eb2af /src/server/game | |
| parent | a86870622dd02921c4d2e32983a5a98ee91e5263 (diff) | |
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)
(cherry picked from commit 7fff83d67526efff63867d41b9e036a19a9287b3)
Diffstat (limited to 'src/server/game')
33 files changed, 980 insertions, 1108 deletions
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 1f1a44c429d..a38bf60e632 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -329,6 +329,9 @@ class TC_GAME_API UnitAI // Called when the dialog status between a player and the creature is requested. virtual uint32 GetDialogStatus(Player* player); + virtual void WaypointStarted(uint32 /*nodeId*/, uint32 /*pathId*/) { } + virtual void WaypointReached(uint32 /*nodeId*/, uint32 /*pathId*/) { } + private: UnitAI(UnitAI const& right) = delete; UnitAI& operator=(UnitAI const& right) = delete; diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 6acb4cd446a..b9da6840474 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -15,21 +15,16 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Npc_EscortAI -SD%Complete: 100 -SDComment: -SDCategory: Npc -EndScriptData */ - #include "ScriptedEscortAI.h" #include "Creature.h" #include "Group.h" #include "Log.h" #include "Map.h" #include "MotionMaster.h" +#include "MovementGenerator.h" #include "ObjectAccessor.h" #include "Player.h" +#include "ScriptSystem.h" #include "World.h" enum Points @@ -38,101 +33,62 @@ enum Points POINT_HOME = 0xFFFFFE }; -npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature), - m_uiWPWaitTimer(2500), - m_uiPlayerCheckTimer(1000), - m_uiEscortState(STATE_ESCORT_NONE), - MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE), - m_pQuestForEscort(nullptr), - m_bIsActiveAttacker(true), - m_bIsRunning(false), - m_bCanInstantRespawn(false), - m_bCanReturnToStart(false), - DespawnAtEnd(true), - DespawnAtFar(true), - ScriptWP(false), - HasImmuneToNPCFlags(false) -{ } - -void npc_escortAI::AttackStart(Unit* who) +EscortAI::EscortAI(Creature* creature) : ScriptedAI(creature), _pauseTimer(2500), _playerCheckTimer(1000), _escortState(STATE_ESCORT_NONE), _maxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE), + _escortQuest(nullptr), _activeAttacker(true), _running(false), _instantRespawn(false), _returnToStart(false), _despawnAtEnd(true), _despawnAtFar(true), _manualPath(false), + _hasImmuneToNPCFlags(false), _started(false), _ended(false), _resume(false) { - if (!who) - return; - - if (me->Attack(who, true)) - { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) - me->GetMotionMaster()->MovementExpired(); - - if (IsCombatMovementAllowed()) - me->GetMotionMaster()->MoveChase(who); - } } -Player* npc_escortAI::GetPlayerForEscort() +Player* EscortAI::GetPlayerForEscort() { - return ObjectAccessor::GetPlayer(*me, m_uiPlayerGUID); + return ObjectAccessor::GetPlayer(*me, _playerGUID); } -//see followerAI -bool npc_escortAI::AssistPlayerInCombatAgainst(Unit* who) +// see followerAI +bool EscortAI::AssistPlayerInCombatAgainst(Unit* who) { if (!who || !who->GetVictim()) return false; - //experimental (unknown) flag not present + if (me->HasReactState(REACT_PASSIVE)) + return false; + + // 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 - if (me->IsFriendlyTo(who)) + // 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, GetMaxPlayerDistance()) && me->IsWithinLOSInMap(who)) { - //already fighting someone? - if (!me->GetVictim()) - { - AttackStart(who); - return true; - } - else - { - me->EngageWithTarget(who); - return true; - } + me->EngageWithTarget(who); + return true; } return false; } -void npc_escortAI::MoveInLineOfSight(Unit* who) +void EscortAI::MoveInLineOfSight(Unit* who) { - if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me)) - { - if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who)) - return; + if (!who) + return; - if (!me->CanFly() && me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) - return; + if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombatAgainst(who)) + return; - if (me->IsHostileTo(who)) - { - float fAttackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who)) - me->EngageWithTarget(who); - } - } + ScriptedAI::MoveInLineOfSight(who); } -void npc_escortAI::JustDied(Unit* /*killer*/) +void EscortAI::JustDied(Unit* /*killer*/) { - if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort) + if (!HasEscortState(STATE_ESCORT_ESCORTING) || !_playerGUID || !_escortQuest) return; if (Player* player = GetPlayerForEscort()) @@ -140,24 +96,26 @@ void npc_escortAI::JustDied(Unit* /*killer*/) if (Group* group = player->GetGroup()) { for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next()) + { if (Player* member = groupRef->GetSource()) if (member->IsInMap(player)) - member->FailQuest(m_pQuestForEscort->GetQuestId()); + member->FailQuest(_escortQuest->GetQuestId()); + } } else - player->FailQuest(m_pQuestForEscort->GetQuestId()); + player->FailQuest(_escortQuest->GetQuestId()); } } -void npc_escortAI::JustAppeared() +void EscortAI::JustAppeared() { - m_uiEscortState = STATE_ESCORT_NONE; + _escortState = STATE_ESCORT_NONE; if (!IsCombatMovementAllowed()) SetCombatMovement(true); - //add a small delay before going to first waypoint, normal in near all cases - m_uiWPWaitTimer = 2500; + // add a small delay before going to first waypoint, normal in near all cases + _pauseTimer = 2000; if (me->GetFaction() != me->GetCreatureTemplate()->faction) me->RestoreFaction(); @@ -165,14 +123,12 @@ void npc_escortAI::JustAppeared() Reset(); } -void npc_escortAI::ReturnToLastPoint() +void EscortAI::ReturnToLastPoint() { - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z); + me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, me->GetHomePosition()); } -void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/) +void EscortAI::EnterEvadeMode(EvadeReason /*why*/) { me->RemoveAllAuras(); me->GetThreatManager().ClearAllThreat(); @@ -183,27 +139,29 @@ void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/) { AddEscortState(STATE_ESCORT_RETURNING); ReturnToLastPoint(); - TC_LOG_DEBUG("scripts", "EscortAI has left combat and is now returning to last point"); + TC_LOG_DEBUG("scripts", "EscortAI::EnterEvadeMode: left combat and is now returning to last point"); } else { me->GetMotionMaster()->MoveTargetedHome(); - if (HasImmuneToNPCFlags) + if (_hasImmuneToNPCFlags) me->SetImmuneToNPC(true); Reset(); } } -bool npc_escortAI::IsPlayerOrGroupInRange() +bool EscortAI::IsPlayerOrGroupInRange() { if (Player* player = GetPlayerForEscort()) { if (Group* group = player->GetGroup()) { for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next()) + { if (Player* member = groupRef->GetSource()) if (me->IsWithinDistInMap(member, GetMaxPlayerDistance())) return true; + } } else if (me->IsWithinDistInMap(player, GetMaxPlayerDistance())) return true; @@ -212,88 +170,77 @@ bool npc_escortAI::IsPlayerOrGroupInRange() return false; } -void npc_escortAI::UpdateAI(uint32 diff) +void EscortAI::UpdateAI(uint32 diff) { - //Waypoint Updating - if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->GetVictim() && m_uiWPWaitTimer && !HasEscortState(STATE_ESCORT_RETURNING)) + // Waypoint Updating + if (HasEscortState(STATE_ESCORT_ESCORTING) && !me->IsEngaged() && !HasEscortState(STATE_ESCORT_RETURNING)) { - if (m_uiWPWaitTimer <= diff) + if (_pauseTimer <= diff) { - //End of the line - if (CurrentWP == WaypointList.end()) + if (!HasEscortState(STATE_ESCORT_PAUSED)) { - if (DespawnAtEnd) - { - TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints"); - - if (m_bCanReturnToStart) - { - float fRetX, fRetY, fRetZ; - me->GetRespawnPosition(fRetX, fRetY, fRetZ); - - me->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ); + _pauseTimer = 0; - m_uiWPWaitTimer = 0; - - TC_LOG_DEBUG("scripts", "EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ); - return; - } + if (_ended) + { + _ended = false; + me->GetMotionMaster()->MoveIdle(); - if (m_bCanInstantRespawn && !sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC)) - { - me->setDeathState(JUST_DIED); - me->Respawn(); - } - else + if (_despawnAtEnd) { - if (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC)) - me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true); - me->DespawnOrUnsummon(); + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: reached end of waypoints, despawning at end"); + if (_returnToStart) + { + Position respawnPosition; + float orientation = 0.f; + me->GetRespawnPosition(respawnPosition.m_positionX, respawnPosition.m_positionY, respawnPosition.m_positionZ, &orientation); + respawnPosition.SetOrientation(orientation); + me->GetMotionMaster()->MovePoint(POINT_HOME, respawnPosition); + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: returning to spawn location: %s", respawnPosition.ToString().c_str()); + } + else if (_instantRespawn) + me->Respawn(true); + else + me->DespawnOrUnsummon(); } - + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: reached end of waypoints"); + RemoveEscortState(STATE_ESCORT_ESCORTING); return; } - else - { - TC_LOG_DEBUG("scripts", "EscortAI reached end of waypoints with Despawn off"); - return; + if (!_started) + { + _started = true; + me->GetMotionMaster()->MovePath(_path, false); + } + else if (_resume) + { + _resume = false; + if (MovementGenerator* movementGenerator = me->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)) + movementGenerator->Resume(0); } - } - - if (!HasEscortState(STATE_ESCORT_PAUSED)) - { - me->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); - TC_LOG_DEBUG("scripts", "EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); - - WaypointStart(CurrentWP->id); - - m_uiWPWaitTimer = 0; } } else - m_uiWPWaitTimer -= diff; + _pauseTimer -= diff; } - //Check if player or any member of his group is within range - if (HasEscortState(STATE_ESCORT_ESCORTING) && !m_uiPlayerGUID.IsEmpty() && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING)) + // Check if player or any member of his group is within range + if (_despawnAtFar && HasEscortState(STATE_ESCORT_ESCORTING) && !_playerGUID.IsEmpty() && !me->GetVictim() && !HasEscortState(STATE_ESCORT_RETURNING)) { - if (m_uiPlayerCheckTimer <= diff) + if (_playerCheckTimer <= diff) { - if (DespawnAtFar && !IsPlayerOrGroupInRange()) + if (!IsPlayerOrGroupInRange()) { - TC_LOG_DEBUG("scripts", "EscortAI failed because player/group was to far away or not found"); + TC_LOG_DEBUG("scripts", "EscortAI::UpdateAI: failed because player/group was to far away or not found"); bool isEscort = false; - if (CreatureData const* cdata = me->GetCreatureData()) - isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (cdata->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC)); + if (CreatureData const* creatureData = me->GetCreatureData()) + isEscort = (sWorld->getBoolConfig(CONFIG_RESPAWN_DYNAMIC_ESCORTNPC) && (creatureData->spawnGroupData->flags & SPAWNGROUP_FLAG_ESCORTQUESTNPC)); - if (m_bCanInstantRespawn && !isEscort) - { - me->setDeathState(JUST_DIED); - me->Respawn(); - } - else if (m_bCanInstantRespawn && isEscort) + if (_instantRespawn && !isEscort) + me->DespawnOrUnsummon(0, Seconds(1)); + else if (_instantRespawn && isEscort) me->GetMap()->RemoveRespawnTime(SPAWN_TYPE_CREATURE, me->GetSpawnId(), true); else me->DespawnOrUnsummon(); @@ -301,16 +248,16 @@ void npc_escortAI::UpdateAI(uint32 diff) return; } - m_uiPlayerCheckTimer = 1000; + _playerCheckTimer = 1000; } else - m_uiPlayerCheckTimer -= diff; + _playerCheckTimer -= diff; } UpdateEscortAI(diff); } -void npc_escortAI::UpdateEscortAI(uint32 /*diff*/) +void EscortAI::UpdateEscortAI(uint32 /*diff*/) { if (!UpdateVictim()) return; @@ -318,121 +265,103 @@ void npc_escortAI::UpdateEscortAI(uint32 /*diff*/) DoMeleeAttackIfReady(); } -void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId) +void EscortAI::MovementInform(uint32 type, uint32 id) { - if (moveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING)) + // no action allowed if there is no escort + if (!HasEscortState(STATE_ESCORT_ESCORTING)) return; - //Combat start position reached, continue waypoint movement - if (pointId == POINT_LAST_POINT) + if (type == POINT_MOTION_TYPE) { - TC_LOG_DEBUG("scripts", "EscortAI has returned to original position before combat"); + if (!_pauseTimer) + _pauseTimer = 2000; - me->SetWalk(!m_bIsRunning); - RemoveEscortState(STATE_ESCORT_RETURNING); - - if (!m_uiWPWaitTimer) - m_uiWPWaitTimer = 1; + // continue waypoint movement + if (id == POINT_LAST_POINT) + { + TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: returned to before combat position"); + me->SetWalk(!_running); + RemoveEscortState(STATE_ESCORT_RETURNING); + } + else if (id == POINT_HOME) + { + TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: returned to home location and restarting waypoint path"); + _started = false; + } } - else if (pointId == POINT_HOME) + else if (type == WAYPOINT_MOTION_TYPE) { - TC_LOG_DEBUG("scripts", "EscortAI has returned to original home location and will continue from beginning of waypoint list."); + ASSERT(id < _path.nodes.size(), "EscortAI::MovementInform: referenced movement id (%u) points to non-existing node in loaded path", id); + WaypointNode waypoint = _path.nodes[id]; - CurrentWP = WaypointList.begin(); - m_uiWPWaitTimer = 1; - } - else - { - //Make sure that we are still on the right waypoint - if (CurrentWP->id != pointId) + TC_LOG_DEBUG("scripts", "EscortAI::MovementInform: waypoint node %u reached", waypoint.id); + + // last point + if (id == _path.nodes.size() - 1) { - TC_LOG_ERROR("misc", "TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u, creature entry %u", pointId, CurrentWP->id, me->GetEntry()); - return; + _started = false; + _ended = true; + _pauseTimer = 1000; } - - TC_LOG_DEBUG("scripts", "EscortAI Waypoint %u reached", CurrentWP->id); - - //Call WP function - WaypointReached(CurrentWP->id); - - m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1; - - ++CurrentWP; } } +///@todo investigate whether if its necessary to handle anything on charm /* -void npc_escortAI::OnPossess(bool apply) +void EscortAI::OnCharmed(bool apply) { - // We got possessed in the middle of being escorted, store the point - // where we left off to come back to when possess is removed - if (HasEscortState(STATE_ESCORT_ESCORTING)) - { - if (apply) - me->GetPosition(LastPos.x, LastPos.y, LastPos.z); - else - { - Returning = true; - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z); - } - } } */ -void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime) +void EscortAI::AddWaypoint(uint32 id, float x, float y, float z, float orientation/* = 0*/, uint32 waitTime/* = 0*/) { - Escort_Waypoint t(id, x, y, z, waitTime); - - WaypointList.push_back(t); - - // i think SD2 no longer uses this function - ScriptWP = true; - /*PointMovement wp; - wp.m_uiCreatureEntry = me->GetEntry(); - wp.m_uiPointId = id; - wp.m_fX = x; - wp.m_fY = y; - wp.m_fZ = z; - wp.m_uiWaitTime = WaitTimeMs; - PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/ + Trinity::NormalizeMapCoord(x); + Trinity::NormalizeMapCoord(y); + + WaypointNode waypoint; + waypoint.id = id; + waypoint.x = x; + waypoint.y = y; + waypoint.z = z; + waypoint.orientation = orientation; + waypoint.moveType = _running ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK; + waypoint.delay = waitTime; + waypoint.eventId = 0; + waypoint.eventChance = 100; + _path.nodes.push_back(std::move(waypoint)); + + _manualPath = true; } -void npc_escortAI::FillPointMovementListForCreature() +void EscortAI::FillPointMovementListForCreature() { - ScriptPointVector const* movePoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry()); - if (!movePoints) + WaypointPath const* path = sScriptSystemMgr->GetPath(me->GetEntry()); + if (!path) return; - for (ScriptPointVector::const_iterator itr = movePoints->begin(); itr != movePoints->end(); ++itr) + for (WaypointNode const& value : path->nodes) { - Escort_Waypoint point(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime); - WaypointList.push_back(point); + WaypointNode node = value; + Trinity::NormalizeMapCoord(node.x); + Trinity::NormalizeMapCoord(node.y); + node.moveType = _running ? WAYPOINT_MOVE_TYPE_RUN : WAYPOINT_MOVE_TYPE_WALK; + + _path.nodes.push_back(std::move(node)); } } -void npc_escortAI::SetRun(bool on) +void EscortAI::SetRun(bool on) { - if (on) - { - if (!m_bIsRunning) - me->SetWalk(false); - else - TC_LOG_DEBUG("scripts", "EscortAI attempt to set run mode, but is already running."); - } - else - { - if (m_bIsRunning) - me->SetWalk(true); - else - TC_LOG_DEBUG("scripts", "EscortAI attempt to set walk mode, but is already walking."); - } + if (on && !_running) + me->SetWalk(false); + else if (!on && _running) + me->SetWalk(true); - m_bIsRunning = on; + _running = on; } /// @todo get rid of this many variables passed in function. -void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */) +void EscortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false */, ObjectGuid playerGUID /* = 0 */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */) { // Queue respawn from the point it starts if (Map* map = me->GetMap()) @@ -452,150 +381,76 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false if (me->GetVictim()) { - TC_LOG_ERROR("scripts.escortai", "TSCR ERROR: EscortAI (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry()); + TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) attempts to Start while in combat", me->GetScriptName().c_str(), me->GetEntry()); return; } if (HasEscortState(STATE_ESCORT_ESCORTING)) { - TC_LOG_ERROR("scripts.escortai", "EscortAI (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry()); + TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) attempts to Start while already escorting", me->GetScriptName().c_str(), me->GetEntry()); return; } - if (!ScriptWP && resetWaypoints) // sd2 never adds wp in script, but tc does - { - if (!WaypointList.empty()) - WaypointList.clear(); + if (!_manualPath && resetWaypoints) FillPointMovementListForCreature(); - } - if (WaypointList.empty()) + if (_path.nodes.empty()) { - TC_LOG_ERROR("scripts", "EscortAI (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).", - me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0); + TC_LOG_ERROR("scripts", "EscortAI::Start: (script: %s, creature entry: %u) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: %u).", me->GetScriptName().c_str(), me->GetEntry(), quest ? quest->GetQuestId() : 0); return; } - //set variables - m_bIsActiveAttacker = isActiveAttacker; - m_bIsRunning = run; - - m_uiPlayerGUID = playerGUID; - m_pQuestForEscort = quest; + // set variables + _activeAttacker = isActiveAttacker; + _running = run; + _playerGUID = playerGUID; + _escortQuest = quest; + _instantRespawn = instantRespawn; + _returnToStart = canLoopPath; - m_bCanInstantRespawn = instantRespawn; - m_bCanReturnToStart = canLoopPath; + if (_returnToStart && _instantRespawn) + TC_LOG_DEBUG("scripts", "EscortAI::Start: (script: %s, creature entry: %u) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.", me->GetScriptName().c_str(), me->GetEntry()); - if (m_bCanReturnToStart && m_bCanInstantRespawn) - TC_LOG_DEBUG("scripts", "EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn."); + me->GetMotionMaster()->MoveIdle(); + me->GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE) - { - me->GetMotionMaster()->MovementExpired(); - me->GetMotionMaster()->MoveIdle(); - TC_LOG_DEBUG("scripts", "EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle."); - } - - //disable npcflags + // disable npcflags me->SetNpcFlags(UNIT_NPC_FLAG_NONE); me->SetNpcFlags2(UNIT_NPC_FLAG_2_NONE); if (me->IsImmuneToNPC()) { - HasImmuneToNPCFlags = true; + _hasImmuneToNPCFlags = true; me->SetImmuneToNPC(false); } - TC_LOG_DEBUG("scripts", "EscortAI started with " UI64FMTD " waypoints. ActiveAttacker = %d, Run = %d, %s", uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID.ToString().c_str()); - - CurrentWP = WaypointList.begin(); + TC_LOG_DEBUG("scripts", "EscortAI::Start: (script: %s, creature entry: %u) started with %u waypoints. ActiveAttacker = %d, Run = %d, Player = %s", me->GetScriptName().c_str(), me->GetEntry(), uint32(_path.nodes.size()), _activeAttacker, _running, _playerGUID.ToString().c_str()); - //Set initial speed - if (m_bIsRunning) - me->SetWalk(false); - else - me->SetWalk(true); + // set initial speed + me->SetWalk(!_running); + _started = false; AddEscortState(STATE_ESCORT_ESCORTING); } -void npc_escortAI::SetEscortPaused(bool on) +void EscortAI::SetEscortPaused(bool on) { if (!HasEscortState(STATE_ESCORT_ESCORTING)) return; if (on) - AddEscortState(STATE_ESCORT_PAUSED); - else - RemoveEscortState(STATE_ESCORT_PAUSED); -} - -bool npc_escortAI::SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation) -{ - me->UpdatePosition(x, y, z, orientation); - return SetNextWaypoint(pointId, false, true); -} - -bool npc_escortAI::SetNextWaypoint(uint32 pointId, bool setPosition, bool resetWaypointsOnFail) -{ - if (!WaypointList.empty()) - WaypointList.clear(); - - FillPointMovementListForCreature(); - - if (WaypointList.empty()) - return false; - - size_t const size = WaypointList.size(); - Escort_Waypoint waypoint(0, 0, 0, 0, 0); - do { - waypoint = WaypointList.front(); - WaypointList.pop_front(); - if (waypoint.id == pointId) - { - if (setPosition) - me->UpdatePosition(waypoint.x, waypoint.y, waypoint.z, me->GetOrientation()); - - CurrentWP = WaypointList.begin(); - return true; - } - } - while (!WaypointList.empty()); - - // we failed. - // we reset the waypoints in the start; if we pulled any, reset it again - if (resetWaypointsOnFail && size != WaypointList.size()) - { - if (!WaypointList.empty()) - WaypointList.clear(); - - FillPointMovementListForCreature(); + AddEscortState(STATE_ESCORT_PAUSED); + if (MovementGenerator* movementGenerator = me->GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)) + movementGenerator->Pause(0); } - - return false; -} - -bool npc_escortAI::GetWaypointPosition(uint32 pointId, float& x, float& y, float& z) -{ - ScriptPointVector const* waypoints = sScriptSystemMgr->GetPointMoveList(me->GetEntry()); - if (!waypoints) - return false; - - for (ScriptPointVector::const_iterator itr = waypoints->begin(); itr != waypoints->end(); ++itr) + else { - if (itr->uiPointId == pointId) - { - x = itr->fX; - y = itr->fY; - z = itr->fZ; - return true; - } + RemoveEscortState(STATE_ESCORT_PAUSED); + _resume = true; } - - return false; } -bool npc_escortAI::IsEscortNPC(bool onlyIfActive) const +bool EscortAI::IsEscortNPC(bool onlyIfActive) const { if (!onlyIfActive) return true; diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h index b91bdd21005..4020d90815e 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h @@ -19,93 +19,59 @@ #define SC_ESCORTAI_H #include "ScriptedCreature.h" -#include "ScriptSystem.h" +#include "WaypointDefines.h" class Quest; #define DEFAULT_MAX_PLAYER_DISTANCE 50 -struct Escort_Waypoint +enum EscortState : uint32 { - Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w) - { - id = _id; - x = _x; - y = _y; - z = _z; - WaitTimeMs = _w; - } - - uint32 id; - float x; - float y; - float z; - uint32 WaitTimeMs; + STATE_ESCORT_NONE = 0x00, // nothing in progress + STATE_ESCORT_ESCORTING = 0x01, // escort is in progress + STATE_ESCORT_RETURNING = 0x02, // escort is returning after being in combat + STATE_ESCORT_PAUSED = 0x04 // escort is paused, wont continue with next waypoint }; -enum eEscortState -{ - STATE_ESCORT_NONE = 0x000, //nothing in progress - STATE_ESCORT_ESCORTING = 0x001, //escort are in progress - STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat - STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed -}; - -struct TC_GAME_API npc_escortAI : public ScriptedAI +struct TC_GAME_API EscortAI : public ScriptedAI { public: - explicit npc_escortAI(Creature* creature); - ~npc_escortAI() { } - - // CreatureAI functions - void AttackStart(Unit* who) override; + explicit EscortAI(Creature* creature); + ~EscortAI() { } + void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI() void MoveInLineOfSight(Unit* who) override; - void JustDied(Unit*) override; - void JustAppeared() override; - void ReturnToLastPoint(); - void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override; - - void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI() - virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc) - void MovementInform(uint32, uint32) override; - // EscortAI functions - void AddWaypoint(uint32 id, float x, float y, float z, uint32 waitTime = 0); // waitTime is in ms - - //this will set the current position to x/y/z/o, and the current WP to pointId. - bool SetNextWaypoint(uint32 pointId, float x, float y, float z, float orientation); + virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc) - //this will set the current position to WP start position (if setPosition == true), - //and the current WP to pointId - bool SetNextWaypoint(uint32 pointId, bool setPosition = true, bool resetWaypointsOnFail = true); - - bool GetWaypointPosition(uint32 pointId, float& x, float& y, float& z); - - virtual void WaypointReached(uint32 pointId) = 0; - virtual void WaypointStart(uint32 /*pointId*/) { } + void AddWaypoint(uint32 id, float x, float y, float z, float orientation = 0.f, uint32 waitTime = 0); // waitTime is in ms void Start(bool isActiveAttacker = true, bool run = false, ObjectGuid playerGUID = ObjectGuid::Empty, Quest const* quest = nullptr, bool instantRespawn = false, bool canLoopPath = false, bool resetWaypoints = true); void SetRun(bool on = true); + void SetEscortPaused(bool on); + void SetPauseTimer(uint32 Timer) { _pauseTimer = Timer; } + + bool HasEscortState(uint32 escortState) { return (_escortState & escortState) != 0; } + virtual bool IsEscorted() const override { return (_escortState & STATE_ESCORT_ESCORTING); } + + void SetMaxPlayerDistance(float newMax) { _maxPlayerDistance = newMax; } + float GetMaxPlayerDistance() const { return _maxPlayerDistance; } - bool HasEscortState(uint32 escortState) { return (m_uiEscortState & escortState) != 0; } - virtual bool IsEscorted() const override { return (m_uiEscortState & STATE_ESCORT_ESCORTING); } + void SetDespawnAtEnd(bool despawn) { _despawnAtEnd = despawn; } + void SetDespawnAtFar(bool despawn) { _despawnAtFar = despawn; } - void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; } - float GetMaxPlayerDistance() const { return MaxPlayerDistance; } + bool GetAttack() const { return _activeAttacker; } // used in EnterEvadeMode override + void SetCanAttack(bool attack) { _activeAttacker = attack; } + + ObjectGuid GetEventStarterGUID() const { return _playerGUID; } - void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; } - void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; } - bool GetAttack() const { return m_bIsActiveAttacker; }//used in EnterEvadeMode override - void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; } - ObjectGuid GetEventStarterGUID() const { return m_uiPlayerGUID; } virtual bool IsEscortNPC(bool isEscorting) const override; protected: @@ -116,27 +82,29 @@ struct TC_GAME_API npc_escortAI : public ScriptedAI bool IsPlayerOrGroupInRange(); void FillPointMovementListForCreature(); - void AddEscortState(uint32 escortState) { m_uiEscortState |= escortState; } - void RemoveEscortState(uint32 escortState) { m_uiEscortState &= ~escortState; } - - ObjectGuid m_uiPlayerGUID; - uint32 m_uiWPWaitTimer; - uint32 m_uiPlayerCheckTimer; - uint32 m_uiEscortState; - float MaxPlayerDistance; - - Quest const* m_pQuestForEscort; //generally passed in Start() when regular escort script. - - std::list<Escort_Waypoint> WaypointList; - std::list<Escort_Waypoint>::iterator CurrentWP; - - bool m_bIsActiveAttacker; //obsolete, determined by faction. - bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK) - bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used) - bool m_bCanReturnToStart; //if creature can walk same path (loop) without despawn. Not for regular escort quests. - bool DespawnAtEnd; - bool DespawnAtFar; - bool ScriptWP; - bool HasImmuneToNPCFlags; + void AddEscortState(uint32 escortState) { _escortState |= escortState; } + void RemoveEscortState(uint32 escortState) { _escortState &= ~escortState; } + + ObjectGuid _playerGUID; + uint32 _pauseTimer; + uint32 _playerCheckTimer; + uint32 _escortState; + float _maxPlayerDistance; + + Quest const* _escortQuest; // generally passed in Start() when regular escort script. + + WaypointPath _path; + + bool _activeAttacker; // obsolete, determined by faction. + bool _running; // all creatures are walking by default (has flag MOVEMENTFLAG_WALK) + bool _instantRespawn; // if creature should respawn instantly after escort over (if not, database respawntime are used) + bool _returnToStart; // if creature can walk same path (loop) without despawn. Not for regular escort quests. + bool _despawnAtEnd; + bool _despawnAtFar; + bool _manualPath; + bool _hasImmuneToNPCFlags; + bool _started; + bool _ended; + bool _resume; }; #endif diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h index 1a35f9c77ad..0212d8e61d3 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h @@ -40,8 +40,6 @@ class TC_GAME_API FollowerAI : public ScriptedAI explicit FollowerAI(Creature* creature); ~FollowerAI() { } - //virtual void WaypointReached(uint32 uiPointId) = 0; - void MovementInform(uint32 motionType, uint32 pointId) override; void AttackStart(Unit*) override; diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index ff2aede2097..9bd9fa7d3cc 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -16,8 +16,9 @@ */ #include "SmartAI.h" -#include "DB2Structure.h" #include "Creature.h" +#include "CreatureGroups.h" +#include "DB2Structure.h" #include "GameObject.h" #include "Group.h" #include "Log.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->m_unitData->NpcFlags[0]; - me->SetNpcFlags((NPCFlags)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->m_unitData->NpcFlags[0]; + me->SetNpcFlags((NPCFlags)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->SetNpcFlags((NPCFlags)mEscortNPCFlags); - mEscortNPCFlags = 0; + me->SetNpcFlags((NPCFlags)_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,81 +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); } } } @@ -449,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*/) @@ -510,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) @@ -543,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 +532,14 @@ void SmartAI::JustAppeared() mDespawnTime = 0; mRespawnTime = 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 +556,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 +577,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 +602,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 +679,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 +693,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 +792,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 +838,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 b892d5705b1..357020f80b2 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); @@ -196,11 +194,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; @@ -210,37 +217,30 @@ 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 mRespawnTime; 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 8b6616a2ee3..44414d5b5ad 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -41,6 +41,7 @@ #include "SpellMgr.h" #include "TemporarySummon.h" #include "Vehicle.h" +#include "WaypointDefines.h" SmartScript::SmartScript() { @@ -2044,7 +2045,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<float>::max(); - WayPoint* closestWp = nullptr; + std::pair<uint32, uint32> closest = { 0, 0 }; for (WorldObject* target : targets) { @@ -2052,29 +2053,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 f1240950f1e..3f0962a20b8 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" #include <algorithm> SmartWaypointMgr* SmartWaypointMgr::instance() @@ -41,15 +42,7 @@ void SmartWaypointMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - for (std::unordered_map<uint32, WPPath*>::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(); WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -63,49 +56,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); + + ++lastId; - last_id++; - (*waypoint_map[entry])[id] = new WayPoint(id, x, y, z); + 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<uint32, WPPath*>::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() @@ -1363,21 +1354,22 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) return false; break; case SMART_ACTION_WP_START: + { + WaypointPath const* path = sSmartWaypointMgr->GetPath(e.action.wpStart.pathID); + if (!path || path->nodes.empty()) { - if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID)) - { - TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature " SI64FMTD " Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID); - return false; - } - if (e.action.wpStart.quest && !IsQuestValid(e, e.action.wpStart.quest)) - return false; - if (e.action.wpStart.reactState > REACT_AGGRESSIVE) - { - TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature " SI64FMTD " Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.reactState); - return false; - } - break; + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature " SI64FMTD " Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID); + return false; } + if (e.action.wpStart.quest && !IsQuestValid(e, e.action.wpStart.quest)) + return false; + if (e.action.wpStart.reactState > REACT_AGGRESSIVE) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature " SI64FMTD " Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.reactState); + return false; + } + break; + } case SMART_ACTION_CREATE_TIMED_EVENT: { if (!IsMinMaxValid(e, e.action.timeEvent.min, e.action.timeEvent.max)) diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 6285726bb33..f358ea81c90 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 <map> #include <string> #include <unordered_map> @@ -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, @@ -1569,8 +1554,6 @@ struct SmartScriptHolder operator bool() const { return entryOrGuid != 0; } }; -typedef std::unordered_map<uint32, WayPoint*> WPPath; - typedef std::vector<WorldObject*> ObjectVector; class ObjectGuidVector @@ -1597,26 +1580,22 @@ typedef std::unordered_map<uint32, ObjectGuidVector> 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<uint32, WPPath*> waypoint_map; + SmartWaypointMgr() { } + ~SmartWaypointMgr() { } + + std::unordered_map<uint32, WaypointPath> _waypointStore; }; +#define sSmartWaypointMgr SmartWaypointMgr::instance() + // all events for a single entry typedef std::vector<SmartScriptHolder> SmartAIEventList; typedef std::vector<SmartScriptHolder> SmartAIEventStoredList; @@ -1693,5 +1672,5 @@ class TC_GAME_API SmartAIMgr }; #define sSmartScriptMgr SmartAIMgr::instance() -#define sSmartWaypointMgr SmartWaypointMgr::instance() + #endif diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index df1112ff27e..232f5f22e2b 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -356,6 +356,21 @@ void Creature::DisappearAndDie() ForcedDespawn(0); } +void Creature::PauseMovement(uint32 timer/* = 0*/, uint8 slot/* = 0*/) +{ + Unit::PauseMovement(timer, slot); + + SetHomePosition(GetPosition()); +} + +bool Creature::IsReturningHome() const +{ + if (GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == HOME_MOTION_TYPE) + return true; + + return false; +} + void Creature::SearchFormation() { if (IsSummon()) @@ -370,6 +385,33 @@ void Creature::SearchFormation() sFormationMgr->AddCreatureToGroup(frmdata->second->leaderGUID, this); } +bool Creature::IsFormationLeader() const +{ + if (!m_formation) + return false; + + return m_formation->IsLeader(this); +} + +void Creature::SignalFormationMovement(Position const& destination, uint32 id/* = 0*/, uint32 moveType/* = 0*/, bool orientation/* = false*/) +{ + if (!m_formation) + return; + + if (!m_formation->IsLeader(this)) + return; + + m_formation->LeaderMoveTo(destination, id, moveType, orientation); +} + +bool Creature::IsFormationLeaderMoveAllowed() const +{ + if (!m_formation) + return false; + + return m_formation->CanLeaderStartMoving(); +} + void Creature::RemoveCorpse(bool setSpawnTime, bool destroyForNearbyPlayers) { if (getDeathState() != CORPSE) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 482518dabd1..2b937e54389 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -35,6 +35,7 @@ class Quest; class Player; class SpellInfo; class WorldSession; + enum MovementGeneratorType : uint8; struct VendorItemCount @@ -295,9 +296,15 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma uint32 GetCurrentWaypointID() const { return m_waypointID; } void UpdateWaypointID(uint32 wpID) { m_waypointID = wpID; } + void PauseMovement(uint32 timer = 0, uint8 slot = 0) override; // timer in ms + bool IsReturningHome() const; + void SearchFormation(); CreatureGroup* GetFormation() { return m_formation; } void SetFormation(CreatureGroup* formation) { m_formation = formation; } + bool IsFormationLeader() const; + void SignalFormationMovement(Position const& destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false); + bool IsFormationLeaderMoveAllowed() const; Unit* SelectVictim(); diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp index 00dac943a08..c18740b673a 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.cpp +++ b/src/server/game/Entities/Creature/CreatureGroups.cpp @@ -257,3 +257,17 @@ void CreatureGroup::LeaderMoveTo(Position destination, uint32 id /*= 0*/, uint32 member->SetHomePosition(dx, dy, dz, pathangle); } } + +bool CreatureGroup::CanLeaderStartMoving() const +{ + for (auto itr = m_members.begin(); itr != m_members.end(); ++itr) + { + if (itr->first != m_leader && itr->first->IsAlive()) + { + if (itr->first->IsEngaged() || itr->first->IsReturningHome()) + return false; + } + } + + return true; +} diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h index 71cabe0bfd8..75c25d79c48 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.h +++ b/src/server/game/Entities/Creature/CreatureGroups.h @@ -83,6 +83,7 @@ class TC_GAME_API CreatureGroup ObjectGuid::LowType GetId() const { return m_groupID; } bool isEmpty() const { return m_members.empty(); } bool isFormed() const { return m_Formed; } + bool IsLeader(Creature const* creature) const { return m_leader == creature; } void AddMember(Creature* member); void RemoveMember(Creature* member); @@ -90,6 +91,7 @@ class TC_GAME_API CreatureGroup void LeaderMoveTo(Position destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false); void MemberEngagingTarget(Creature* member, Unit* target); + bool CanLeaderStartMoving() const; }; #define sFormationMgr FormationMgr::instance() diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ff0e1ea8cb3..390d8ee7b3c 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -45,6 +45,7 @@ #include "LootPackets.h" #include "MiscPackets.h" #include "MotionMaster.h" +#include "MovementGenerator.h" #include "MovementPackets.h" #include "MoveSpline.h" #include "MoveSplineInit.h" @@ -10703,6 +10704,26 @@ void Unit::StopMoving() init.Stop(); } +void Unit::PauseMovement(uint32 timer/* = 0*/, uint8 slot/* = 0*/) +{ + if (slot >= MAX_MOTION_SLOT) + return; + + if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(slot)) + movementGenerator->Pause(timer); + + StopMoving(); +} + +void Unit::ResumeMovement(uint32 timer/* = 0*/, uint8 slot/* = 0*/) +{ + if (slot >= MAX_MOTION_SLOT) + return; + + if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(slot)) + movementGenerator->Resume(timer); +} + bool Unit::IsSitState() const { UnitStandStateType s = GetStandState(); @@ -11866,8 +11887,14 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au if (GetTypeId() == TYPEID_UNIT) { + if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE)) + movementGenerator->Pause(0); + + GetMotionMaster()->Clear(MOTION_SLOT_ACTIVE); + + StopMoving(); + ToCreature()->AI()->OnCharmed(true); - GetMotionMaster()->MoveIdle(); } else if (Player* player = ToPlayer()) { @@ -11981,6 +12008,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) else RestoreFaction(); + ///@todo Handle SLOT_IDLE motion resume GetMotionMaster()->InitDefault(); if (Creature* creature = ToCreature()) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index fb04300b841..fb2af4bb11c 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1909,6 +1909,8 @@ class TC_GAME_API Unit : public WorldObject bool IsStopped() const { return !(HasUnitState(UNIT_STATE_MOVING)); } void StopMoving(); + virtual void PauseMovement(uint32 timer = 0, uint8 slot = 0); // timer in ms + void ResumeMovement(uint32 timer = 0, uint8 slot = 0); void AddUnitMovementFlag(uint32 f) { m_movementInfo.AddMovementFlag(f); } void RemoveUnitMovementFlag(uint32 f) { m_movementInfo.RemoveMovementFlag(f); } diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 3dc89e4443f..463070837b8 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -38,6 +38,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "World.h" void WorldSession::HandleBattlemasterHelloOpcode(WorldPackets::NPC::Hello& hello) { @@ -46,7 +47,7 @@ void WorldSession::HandleBattlemasterHelloOpcode(WorldPackets::NPC::Hello& hello return; // Stop the npc if moving - unit->StopMoving(); + unit->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); BattlegroundTypeId bgTypeId = sBattlegroundMgr->GetBattleMasterBG(unit->GetEntry()); diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 66f9beb12d2..225547f0b5c 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -29,6 +29,7 @@ #include "Opcodes.h" #include "Player.h" #include "SpellMgr.h" +#include "World.h" #include "WorldSession.h" void WorldSession::HandleSplitItemOpcode(WorldPackets::Item::SplitItem& splitItem) @@ -599,8 +600,7 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); // Stop the npc if moving - if (vendor->HasUnitState(UNIT_STATE_MOVING)) - vendor->StopMoving(); + vendor->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); VendorItemData const* vendorItems = vendor->GetVendorItems(); uint32 rawItemCount = vendorItems ? vendorItems->GetItemCount() : 0; diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 29324f41107..66e891fe3ee 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -30,6 +30,8 @@ #include "Opcodes.h" #include "Player.h" #include "SpellInfo.h" +#include "MotionMaster.h" +#include "MovementGenerator.h" #include "Transport.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" @@ -153,8 +155,8 @@ void WorldSession::HandleMoveWorldportAck() if (!seamlessTeleport) { // short preparations to continue flight - FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top()); - flight->Initialize(GetPlayer()); + MovementGenerator* movementGenerator = GetPlayer()->GetMotionMaster()->top(); + movementGenerator->Initialize(GetPlayer()); } return; } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index e5e5a8f97c3..16c6ca0b1bf 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -44,6 +44,7 @@ #include "SpellInfo.h" #include "SpellMgr.h" #include "Trainer.h" +#include "World.h" #include "WorldPacket.h" enum class StableResult : uint8 @@ -167,9 +168,8 @@ void WorldSession::HandleGossipHelloOpcode(WorldPackets::NPC::Hello& packet) //if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) // GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - // and if he has pure gossip or is banker and moves or is tabard designer? - //if (unit->IsArmorer() || unit->IsCivilian() || unit->IsQuestGiver() || unit->IsServiceProvider() || unit->IsGuard()) - unit->StopMoving(); + // Stop the npc if moving + unit->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); // If spiritguide, no need for gossip menu, just put player into resurrect queue if (unit->IsSpiritGuide()) diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index d3a1dd87fb8..fe6826441d1 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -87,8 +87,9 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPackets::Quest::QuestGiverHe // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + // Stop the npc if moving - creature->StopMoving(); + creature->PauseMovement(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); _player->PlayerTalkClass->ClearMenus(); if (creature->GetAI()->GossipHello(_player)) diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index 066fc7910a4..c34270c3836 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -19,9 +19,11 @@ #include "Common.h" #include "ConditionMgr.h" #include "Containers.h" +#include "Creature.h" #include "DatabaseEnv.h" #include "DB2Stores.h" #include "Log.h" +#include "MotionMaster.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" @@ -128,8 +130,7 @@ void WorldSession::SendDoFlight(uint32 mountDisplayId, uint32 path, uint32 pathN if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - while (GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) - GetPlayer()->GetMotionMaster()->MovementExpired(false); + GetPlayer()->GetMotionMaster()->Clear(MOTION_SLOT_CONTROLLED); if (mountDisplayId) GetPlayer()->Mount(mountDisplayId); diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 0e6b216aa44..acea74214f5 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -16,24 +16,27 @@ */ #include "MotionMaster.h" -#include "CreatureAISelector.h" -#include "Creature.h" -#include "ScriptSystem.h" -#include "Log.h" -#include "Map.h" #include "ConfusedMovementGenerator.h" +#include "Creature.h" +#include "CreatureAISelector.h" +#include "DB2Stores.h" #include "FleeingMovementGenerator.h" +#include "FormationMovementGenerator.h" #include "HomeMovementGenerator.h" #include "IdleMovementGenerator.h" -#include "PointMovementGenerator.h" -#include "TargetedMovementGenerator.h" -#include "WaypointMovementGenerator.h" -#include "RandomMovementGenerator.h" -#include "SplineChainMovementGenerator.h" -#include "FormationMovementGenerator.h" +#include "Log.h" +#include "Map.h" #include "MoveSpline.h" #include "MoveSplineInit.h" #include "PathGenerator.h" +#include "PetDefines.h" +#include "Player.h" +#include "PointMovementGenerator.h" +#include "RandomMovementGenerator.h" +#include "ScriptSystem.h" +#include "SplineChainMovementGenerator.h" +#include "TargetedMovementGenerator.h" +#include "WaypointMovementGenerator.h" inline MovementGenerator* GetIdleMovementGenerator() { @@ -687,15 +690,23 @@ void MotionMaster::MoveDistract(uint32 timer) Mutate(mgen, MOTION_SLOT_CONTROLLED); } -void MotionMaster::MovePath(uint32 path_id, bool repeatable) +void MotionMaster::MovePath(uint32 pathId, bool repeatable) { - if (!path_id) + if (!pathId) return; - Mutate(new WaypointMovementGenerator<Creature>(path_id, repeatable), MOTION_SLOT_IDLE); + Mutate(new WaypointMovementGenerator<Creature>(pathId, repeatable), MOTION_SLOT_IDLE); TC_LOG_DEBUG("misc", "%s starts moving over path (Id:%u, repeatable: %s).", - _owner->GetGUID().ToString().c_str(), path_id, repeatable ? "YES" : "NO"); + _owner->GetGUID().ToString().c_str(), pathId, repeatable ? "YES" : "NO"); +} + +void MotionMaster::MovePath(WaypointPath& path, bool repeatable) +{ + Mutate(new WaypointMovementGenerator<Creature>(path, repeatable), MOTION_SLOT_IDLE); + + TC_LOG_DEBUG("misc", "%s start moving over path (repeatable: %s)", + _owner->GetGUID().ToString().c_str(), repeatable ? "YES" : "NO"); } void MotionMaster::MoveRotate(uint32 time, RotateDirection direction) diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index 01d6574945f..d8d2e4e1258 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -31,6 +31,7 @@ class PathGenerator; struct Position; struct SplineChainLink; struct SplineChainResumeInfo; +struct WaypointPath; namespace G3D { @@ -70,9 +71,9 @@ enum MovementGeneratorType : uint8 MAX_MOTION_TYPE // limit }; -enum MovementSlot +enum MovementSlot : uint8 { - MOTION_SLOT_IDLE, + MOTION_SLOT_IDLE = 0, MOTION_SLOT_ACTIVE, MOTION_SLOT_CONTROLLED, MAX_MOTION_SLOT @@ -175,7 +176,8 @@ class TC_GAME_API MotionMaster void MoveSeekAssistanceDistract(uint32 timer); void MoveTaxiFlight(uint32 path, uint32 pathnode); void MoveDistract(uint32 time); - void MovePath(uint32 path_id, bool repeatable); + void MovePath(uint32 pathId, bool repeatable); + void MovePath(WaypointPath& path, bool repeatable); void MoveRotate(uint32 time, RotateDirection direction); void MoveFormation(uint32 id, Position destination, uint32 moveType, bool forceRun = false, bool forceOrientation = false); diff --git a/src/server/game/Movement/MovementGenerator.h b/src/server/game/Movement/MovementGenerator.h index 4af0709aa06..3064a3d967e 100755 --- a/src/server/game/Movement/MovementGenerator.h +++ b/src/server/game/Movement/MovementGenerator.h @@ -35,10 +35,11 @@ class TC_GAME_API MovementGenerator virtual void Finalize(Unit*) = 0; virtual void Reset(Unit*) = 0; virtual bool Update(Unit*, uint32 diff) = 0; - virtual MovementGeneratorType GetMovementGeneratorType() const = 0; virtual void UnitSpeedChanged() { } + virtual void Pause(uint32/* timer = 0*/) { } // timer in ms + virtual void Resume(uint32/* overrideTimer = 0*/) { } // timer in ms // used by Evade code for select point to evade with expected restart default movement virtual bool GetResetPosition(Unit*, float& /*x*/, float& /*y*/, float& /*z*/) { return false; } diff --git a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp index ff3a2699128..3a35e22c068 100755 --- a/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/PointMovementGenerator.cpp @@ -17,7 +17,6 @@ #include "CreatureAI.h" #include "Creature.h" -#include "CreatureGroups.h" #include "Player.h" #include "MoveSplineInit.h" #include "MoveSpline.h" @@ -59,8 +58,7 @@ void PointMovementGenerator<T>::DoInitialize(T* owner) // Call for creature group update if (Creature* creature = owner->ToCreature()) - if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(_destination, _movementId); + creature->SignalFormationMovement(_destination, _movementId); } template<class T> @@ -94,8 +92,7 @@ bool PointMovementGenerator<T>::DoUpdate(T* owner, uint32 /*diff*/) // Call for creature group update if (Creature* creature = owner->ToCreature()) - if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(_destination, _movementId); + creature->SignalFormationMovement(_destination, _movementId); } return !owner->movespline->Finalized(); diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 7b0025d644f..ca95243d507 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -16,7 +16,6 @@ */ #include "Creature.h" -#include "CreatureGroups.h" #include "Map.h" #include "MoveSplineInit.h" #include "MoveSpline.h" @@ -115,8 +114,7 @@ void RandomMovementGenerator<Creature>::SetRandomLocation(Creature* owner) _timer.Reset(traveltime + resetTimer); // Call for creature group update - if (owner->GetFormation() && owner->GetFormation()->getLeader() == owner) - owner->GetFormation()->LeaderMoveTo(position); + owner->SignalFormationMovement(position); } template<class T> diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 6ad3774b35e..8780531443b 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -16,98 +16,137 @@ */ #include "WaypointMovementGenerator.h" +#include "Creature.h" #include "CreatureAI.h" -#include "CreatureGroups.h" #include "Log.h" #include "MapManager.h" #include "MoveSpline.h" #include "MoveSplineInit.h" #include "ObjectMgr.h" +#include "Player.h" #include "Transport.h" +#include "WaypointDefines.h" +#include "WaypointManager.h" #include "World.h" +WaypointMovementGenerator<Creature>::WaypointMovementGenerator(uint32 pathId /*= 0*/, bool repeating /*= true*/) : _nextMoveTime(0), _recalculateSpeed(false), _isArrivalDone(false), _pathId(pathId), _repeating(repeating), _loadedFromDB(true), _stalled(false) +{ +} + +WaypointMovementGenerator<Creature>::WaypointMovementGenerator(WaypointPath& path, bool repeating) +{ + _path = &path; + _nextMoveTime = 0; + _recalculateSpeed = false; + _isArrivalDone = false; + _pathId = 0; + _repeating = repeating; + _loadedFromDB = false; + _stalled = false; +} + +WaypointMovementGenerator<Creature>::~WaypointMovementGenerator() +{ + _path = nullptr; +} + void WaypointMovementGenerator<Creature>::LoadPath(Creature* creature) { - if (!path_id) - path_id = creature->GetWaypointPath(); + if (_loadedFromDB) + { + if (!_pathId) + _pathId = creature->GetWaypointPath(); - i_path = sWaypointMgr->GetPath(path_id); + _path = sWaypointMgr->GetPath(_pathId); + } - if (!i_path) + if (!_path) { // No path id found for entry - TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature %s (%s DB GUID: " UI64FMTD ") doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetGUID().ToString().c_str(), creature->GetSpawnId(), path_id); + TC_LOG_ERROR("sql.sql", "WaypointMovementGenerator::LoadPath: creature %s (%s DB GUID: " UI64FMTD ") doesn't have waypoint path id: %u", creature->GetName().c_str(), creature->GetGUID().ToString().c_str(), creature->GetSpawnId(), _pathId); return; } - StartMoveNow(creature); + _nextMoveTime.Reset(3000); + + if (CanMove(creature)) + StartMoveNow(creature); } void WaypointMovementGenerator<Creature>::DoInitialize(Creature* creature) { LoadPath(creature); - creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); } void WaypointMovementGenerator<Creature>::DoFinalize(Creature* creature) { - creature->ClearUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); + creature->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE); creature->SetWalk(false); } void WaypointMovementGenerator<Creature>::DoReset(Creature* creature) { - creature->AddUnitState(UNIT_STATE_ROAMING|UNIT_STATE_ROAMING_MOVE); - StartMoveNow(creature); + if (CanMove(creature)) + StartMoveNow(creature); } void WaypointMovementGenerator<Creature>::OnArrived(Creature* creature) { - if (!i_path || i_path->empty()) - return; - if (m_isArrivalDone) + if (!_path || _path->nodes.empty()) return; - m_isArrivalDone = true; - - if (i_path->at(i_currentNode)->event_id && urand(0, 99) < i_path->at(i_currentNode)->event_chance) + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + if (waypoint.delay) { - TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", i_path->at(i_currentNode)->event_id, i_currentNode, creature->GetGUID().ToString().c_str()); creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - creature->GetMap()->ScriptsStart(sWaypointScripts, i_path->at(i_currentNode)->event_id, creature, nullptr); + _nextMoveTime.Reset(waypoint.delay); } - // Inform script - MovementInform(creature); - creature->UpdateWaypointID(i_currentNode); - - if (i_path->at(i_currentNode)->delay) + if (waypoint.eventId && urand(0, 99) < waypoint.eventChance) { + TC_LOG_DEBUG("maps.script", "Creature movement start script %u at point %u for %s.", waypoint.eventId, _currentNode, creature->GetGUID().ToString().c_str()); creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); - Stop(i_path->at(i_currentNode)->delay); + creature->GetMap()->ScriptsStart(sWaypointScripts, waypoint.eventId, creature, nullptr); } + + // inform AI + if (creature->AI()) + { + creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, _currentNode); + + ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::OnArrived: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id); + creature->AI()->WaypointReached(_path->nodes[_currentNode].id, _path->id); + } + + creature->UpdateWaypointID(_currentNode); } bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) { - if (!i_path || i_path->empty()) + if (!creature || !creature->IsAlive()) return false; - if (Stopped()) - return true; + if (!_path || _path->nodes.empty()) + return false; - if (Stopped()) + // if the owner is the leader of its formation, check members status + if (creature->IsFormationLeader() && !creature->IsFormationLeaderMoveAllowed()) + { + _nextMoveTime.Reset(1000); return true; + } bool transportPath = creature->GetTransport() != nullptr; - if (m_isArrivalDone) + if (_isArrivalDone) { - if ((i_currentNode == i_path->size() - 1) && !repeating) // If that's our last waypoint + if ((_currentNode == _path->nodes.size() - 1) && !_repeating) // If that's our last waypoint { - float x = i_path->at(i_currentNode)->x; - float y = i_path->at(i_currentNode)->y; - float z = i_path->at(i_currentNode)->z; + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + + float x = waypoint.x; + float y = waypoint.y; + float z = waypoint.z; float o = creature->GetOrientation(); if (!transportPath) @@ -125,21 +164,27 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) transportPath = false; // else if (vehicle) - this should never happen, vehicle offsets are const } - - creature->GetMotionMaster()->Initialize(); return false; } - i_currentNode = (i_currentNode+1) % i_path->size(); + _currentNode = (_currentNode + 1) % _path->nodes.size(); + + // inform AI + if (creature->AI()) + { + ASSERT(_currentNode < _path->nodes.size(), "WaypointMovementGenerator::StartMove: tried to reference a node id (%u) which is not included in path (%u)", _currentNode, _path->id); + creature->AI()->WaypointStarted(_path->nodes[_currentNode].id, _path->id); + } } - WaypointData const* node = i_path->at(i_currentNode); + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + Position formationDest(waypoint.x, waypoint.y, waypoint.z, (waypoint.orientation && waypoint.delay) ? waypoint.orientation : 0.0f); - m_isArrivalDone = false; + _isArrivalDone = false; + _recalculateSpeed = false; creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); - Position formationDest(node->x, node->y, node->z, (node->orientation && node->delay) ? node->orientation : 0.0f); Movement::MoveSplineInit init(creature); //! If creature is on transport, we assume waypoints set in DB are already transport offsets @@ -156,13 +201,13 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) //! Do not use formationDest here, MoveTo requires transport offsets due to DisableTransportPathTransformations() call //! but formationDest contains global coordinates - init.MoveTo(node->x, node->y, node->z); + init.MoveTo(waypoint.x, waypoint.y, waypoint.z); //! Accepts angles such as 0.00001 and -0.00001, 0 must be ignored, default value in waypoint table - if (node->orientation && node->delay) - init.SetFacing(node->orientation); + if (waypoint.orientation && waypoint.delay) + init.SetFacing(waypoint.orientation); - switch (node->move_type) + switch (waypoint.moveType) { case WAYPOINT_MOVE_TYPE_LAND: init.SetAnimation(Movement::ToGround); @@ -176,47 +221,57 @@ bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) case WAYPOINT_MOVE_TYPE_WALK: init.SetWalk(true); break; + default: + break; } init.Launch(); - // Call for creature group update - if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) - creature->GetFormation()->LeaderMoveTo(formationDest, node->id, node->move_type, (node->orientation && node->delay) ? true : false); + // inform formation + creature->SignalFormationMovement(formationDest, waypoint.id, waypoint.moveType, (waypoint.orientation && waypoint.delay) ? true : false); return true; } bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 diff) { - // Waypoint movement can be switched on/off - // This is quite handy for escort quests and other stuff - if (creature->HasUnitState(UNIT_STATE_NOT_MOVE)) + if (!creature || !creature->IsAlive()) + return false; + + if (_stalled || creature->HasUnitState(UNIT_STATE_NOT_MOVE) || creature->IsMovementPreventedByCasting()) { - creature->ClearUnitState(UNIT_STATE_ROAMING_MOVE); + creature->StopMoving(); return true; } + // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) + if (!_path || _path->nodes.empty()) return false; - if (Stopped()) + if (!_nextMoveTime.Passed()) { - if (CanMove(diff)) - return StartMove(creature); + _nextMoveTime.Update(diff); + if (_nextMoveTime.Passed()) + return StartMoveNow(creature); } else { // Set home position at place on waypoint movement. - if (!creature->GetTransGUID()) + if (creature->GetTransGUID().IsEmpty()) creature->SetHomePosition(creature->GetPosition()); - if (creature->IsStopped()) - Stop(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); - else if (creature->movespline->Finalized()) + if (creature->movespline->Finalized()) { OnArrived(creature); - return StartMove(creature); + _isArrivalDone = true; + + if (_nextMoveTime.Passed()) + return StartMove(creature); + } + else if (_recalculateSpeed) + { + if (_nextMoveTime.Passed()) + StartMove(creature); } } return true; @@ -225,47 +280,79 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di void WaypointMovementGenerator<Creature>::MovementInform(Creature* creature) { if (creature->AI()) - creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode); + creature->AI()->MovementInform(WAYPOINT_MOTION_TYPE, _currentNode); } bool WaypointMovementGenerator<Creature>::GetResetPos(Creature*, float& x, float& y, float& z) { // prevent a crash at empty waypoint path. - if (!i_path || i_path->empty()) + if (!_path || _path->nodes.empty()) return false; - const WaypointData* node = i_path->at(i_currentNode); - x = node->x; y = node->y; z = node->z; + WaypointNode const &waypoint = _path->nodes.at(_currentNode); + + x = waypoint.x; + y = waypoint.y; + z = waypoint.z; return true; } +void WaypointMovementGenerator<Creature>::Pause(uint32 timer/* = 0*/) +{ + _stalled = timer ? false : true; + _nextMoveTime.Reset(timer ? timer : 1); +} + +void WaypointMovementGenerator<Creature>::Resume(uint32 overrideTimer/* = 0*/) +{ + _stalled = false; + if (overrideTimer) + _nextMoveTime.Reset(overrideTimer); +} + +bool WaypointMovementGenerator<Creature>::CanMove(Creature* creature) +{ + return _nextMoveTime.Passed() && !creature->HasUnitState(UNIT_STATE_NOT_MOVE) && !creature->IsMovementPreventedByCasting(); +} //----------------------------------------------------// +#define FLIGHT_TRAVEL_UPDATE 100 +#define TIMEDIFF_NEXT_WP 250 +#define SKIP_SPLINE_POINT_DISTANCE_SQ (40.f * 40.f) +#define PLAYER_FLIGHT_SPEED 32.0f + uint32 FlightPathMovementGenerator::GetPathAtMapEnd() const { - if (i_currentNode >= i_path.size()) - return i_path.size(); + if (_currentNode >= _path.size()) + return _path.size(); - uint32 curMapId = i_path[i_currentNode]->ContinentID; - for (uint32 i = i_currentNode; i < i_path.size(); ++i) - if (i_path[i]->ContinentID != curMapId) - return i; + uint32 curMapId = _path[_currentNode]->ContinentID; + for (uint32 itr = _currentNode; itr < _path.size(); ++itr) + if (_path[itr]->ContinentID != curMapId) + return itr; - return i_path.size(); + return _path.size(); } -#define SKIP_SPLINE_POINT_DISTANCE_SQ (40.0f * 40.0f) - bool IsNodeIncludedInShortenedPath(TaxiPathNodeEntry const* p1, TaxiPathNodeEntry const* p2) { return p1->ContinentID != p2->ContinentID || std::pow(p1->Loc.X - p2->Loc.X, 2) + std::pow(p1->Loc.Y - p2->Loc.Y, 2) > SKIP_SPLINE_POINT_DISTANCE_SQ; } +FlightPathMovementGenerator::FlightPathMovementGenerator() +{ + _currentNode = 0; + _endGridX = 0.0f; + _endGridY = 0.0f; + _endMapId = 0; + _preloadTargetNode = 0; +} + void FlightPathMovementGenerator::LoadPath(Player* player, uint32 startNode /*= 0*/) { - i_path.clear(); - i_currentNode = startNode; + _path.clear(); + _currentNode = startNode; _pointsForPathSwitch.clear(); std::deque<uint32> const& taxi = player->m_taxi.GetPath(); float discount = player->GetReputationPriceDiscount(player->m_taxi.GetFlightMasterFactionTemplate()); @@ -284,24 +371,24 @@ void FlightPathMovementGenerator::LoadPath(Player* player, uint32 startNode /*= bool passedPreviousSegmentProximityCheck = false; for (uint32 i = 0; i < nodes.size(); ++i) { - if (passedPreviousSegmentProximityCheck || !src || i_path.empty() || IsNodeIncludedInShortenedPath(i_path.back(), nodes[i])) + if (passedPreviousSegmentProximityCheck || !src || _path.empty() || IsNodeIncludedInShortenedPath(_path.back(), nodes[i])) { if ((!src || (IsNodeIncludedInShortenedPath(start, nodes[i]) && i >= 2)) && (dst == taxi.size() - 1 || (IsNodeIncludedInShortenedPath(end, nodes[i]) && i < nodes.size() - 1))) { passedPreviousSegmentProximityCheck = true; - i_path.push_back(nodes[i]); + _path.push_back(nodes[i]); } } else { - i_path.pop_back(); + _path.pop_back(); --_pointsForPathSwitch.back().PathIndex; } } } - _pointsForPathSwitch.push_back({ uint32(i_path.size() - 1), int64(ceil(cost * discount)) }); + _pointsForPathSwitch.push_back({ uint32(_path.size() - 1), int64(ceil(cost * discount)) }); } } @@ -333,8 +420,6 @@ void FlightPathMovementGenerator::DoFinalize(Player* player) player->RestoreDisplayId(); } -#define PLAYER_FLIGHT_SPEED 30.0f - void FlightPathMovementGenerator::DoReset(Player* player) { player->getHostileRefManager().setOnlineOfflineState(false); @@ -345,7 +430,7 @@ void FlightPathMovementGenerator::DoReset(Player* player) uint32 end = GetPathAtMapEnd(); for (uint32 i = GetCurrentNode(); i != end; ++i) { - G3D::Vector3 vertice(i_path[i]->Loc.X, i_path[i]->Loc.Y, i_path[i]->Loc.Z); + G3D::Vector3 vertice(_path[i]->Loc.X, _path[i]->Loc.Y, _path[i]->Loc.Z); init.Path().push_back(vertice); } init.SetFirstPointId(GetCurrentNode()); @@ -360,13 +445,13 @@ void FlightPathMovementGenerator::DoReset(Player* player) bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/) { uint32 pointId = (uint32)player->movespline->currentPathIdx(); - if (pointId > i_currentNode) + if (pointId > _currentNode) { bool departureEvent = true; do { - DoEventIfAny(player, i_path[i_currentNode], departureEvent); - while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front().PathIndex <= i_currentNode) + DoEventIfAny(player, _path[_currentNode], departureEvent); + while (!_pointsForPathSwitch.empty() && _pointsForPathSwitch.front().PathIndex <= _currentNode) { _pointsForPathSwitch.pop_front(); player->m_taxi.NextTaxiDestination(); @@ -377,31 +462,31 @@ bool FlightPathMovementGenerator::DoUpdate(Player* player, uint32 /*diff*/) } } - if (pointId == i_currentNode) + if (pointId == _currentNode) break; - if (i_currentNode == _preloadTargetNode) + if (_currentNode == _preloadTargetNode) PreloadEndGrid(); - i_currentNode += (uint32)departureEvent; + _currentNode += departureEvent ? 1 : 0; departureEvent = !departureEvent; } while (true); } - return i_currentNode < (i_path.size() - 1); + return _currentNode < (_path.size() - 1); } void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport() { - if (i_path.empty() || i_currentNode >= i_path.size()) + if (_path.empty() || _currentNode >= _path.size()) return; - uint32 map0 = i_path[i_currentNode]->ContinentID; - for (size_t i = i_currentNode + 1; i < i_path.size(); ++i) + uint32 map0 = _path[_currentNode]->ContinentID; + for (size_t i = _currentNode + 1; i < _path.size(); ++i) { - if (i_path[i]->ContinentID != map0) + if (_path[i]->ContinentID != map0) { - i_currentNode = i; + _currentNode = i; return; } } @@ -418,7 +503,7 @@ void FlightPathMovementGenerator::DoEventIfAny(Player* player, TaxiPathNodeEntry bool FlightPathMovementGenerator::GetResetPos(Player*, float& x, float& y, float& z) { - TaxiPathNodeEntry const* node = i_path[i_currentNode]; + TaxiPathNodeEntry const* node = _path[_currentNode]; x = node->Loc.X; y = node->Loc.Y; z = node->Loc.Z; @@ -429,11 +514,11 @@ void FlightPathMovementGenerator::InitEndGridInfo() { /*! Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will be reinitialized for each flightmaster at the end of each spline (or stop) in the flight. */ - uint32 nodeCount = i_path.size(); //! Number of nodes in path. - _endMapId = i_path[nodeCount - 1]->ContinentID; //! MapId of last node + uint32 nodeCount = _path.size(); //! Number of nodes in path. + _endMapId = _path[nodeCount - 1]->ContinentID; //! MapId of last node _preloadTargetNode = nodeCount - 3; - _endGridX = i_path[nodeCount - 1]->Loc.X; - _endGridY = i_path[nodeCount - 1]->Loc.Y; + _endGridX = _path[nodeCount - 1]->Loc.X; + _endGridY = _path[nodeCount - 1]->Loc.Y; } void FlightPathMovementGenerator::PreloadEndGrid() @@ -444,7 +529,7 @@ void FlightPathMovementGenerator::PreloadEndGrid() // Load the grid if (endMap) { - TC_LOG_DEBUG("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(i_path.size() - 1)); + TC_LOG_DEBUG("misc", "Preloading rid (%f, %f) for map %u at node index %u/%u", _endGridX, _endGridY, _endMapId, _preloadTargetNode, (uint32)(_path.size() - 1)); endMap->LoadGrid(_endGridX, _endGridY); } else diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index de0e68f7cd1..f6d3b6a3c9e 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -18,103 +18,89 @@ #ifndef TRINITY_WAYPOINTMOVEMENTGENERATOR_H #define TRINITY_WAYPOINTMOVEMENTGENERATOR_H -/** @page PathMovementGenerator is used to generate movements +/** + * @page PathMovementGenerator is used to generate movements * of waypoints and flight paths. Each serves the purpose * of generate activities so that it generates updated * packets for the players. */ #include "MovementGenerator.h" -#include "Creature.h" -#include "DB2Stores.h" -#include "Player.h" #include "Timer.h" -#include "WaypointManager.h" -#define FLIGHT_TRAVEL_UPDATE 100 -#define TIMEDIFF_NEXT_WP 250 +class Creature; +class Player; +struct TaxiPathNodeEntry; +struct WaypointPath; -template<class T, class P> +template<class Entity, class BasePath> class PathMovementBase { public: - PathMovementBase() : i_path(), i_currentNode(0) { } + PathMovementBase() : _path(), _currentNode(0) { } virtual ~PathMovementBase() { }; - uint32 GetCurrentNode() const { return i_currentNode; } + uint32 GetCurrentNode() const { return _currentNode; } protected: - P i_path; - uint32 i_currentNode; + BasePath _path; + uint32 _currentNode; }; template<class T> class WaypointMovementGenerator; template<> -class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium< Creature, WaypointMovementGenerator<Creature> >, - public PathMovementBase<Creature, WaypointPath const*> +class WaypointMovementGenerator<Creature> : public MovementGeneratorMedium<Creature, WaypointMovementGenerator<Creature>>, public PathMovementBase<Creature, WaypointPath const*> { public: - WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true) - : i_nextMoveTime(0), m_isArrivalDone(false), path_id(_path_id), repeating(_repeating) { } - ~WaypointMovementGenerator() { i_path = nullptr; } + explicit WaypointMovementGenerator(uint32 pathId = 0, bool repeating = true); + explicit WaypointMovementGenerator(WaypointPath& path, bool repeating = true); + + ~WaypointMovementGenerator(); + void DoInitialize(Creature*); void DoFinalize(Creature*); void DoReset(Creature*); bool DoUpdate(Creature*, uint32 diff); - void MovementInform(Creature*); - MovementGeneratorType GetMovementGeneratorType() const override { return WAYPOINT_MOTION_TYPE; } + void UnitSpeedChanged() override { _recalculateSpeed = true; } + void Pause(uint32 timer = 0) override; + void Resume(uint32 overrideTimer = 0) override; - // now path movement implmementation - void LoadPath(Creature*); + void MovementInform(Creature*); bool GetResetPos(Creature*, float& x, float& y, float& z); private: - - void Stop(int32 time) { i_nextMoveTime.Reset(time);} - - bool Stopped() { return !i_nextMoveTime.Passed();} - - bool CanMove(int32 diff) - { - i_nextMoveTime.Update(diff); - return i_nextMoveTime.Passed(); - } - + void LoadPath(Creature*); void OnArrived(Creature*); bool StartMove(Creature*); - - void StartMoveNow(Creature* creature) + bool CanMove(Creature*); + bool StartMoveNow(Creature* creature) { - i_nextMoveTime.Reset(0); - StartMove(creature); + _nextMoveTime.Reset(0); + return StartMove(creature); } - TimeTrackerSmall i_nextMoveTime; - bool m_isArrivalDone; - uint32 path_id; - bool repeating; + TimeTrackerSmall _nextMoveTime; + bool _recalculateSpeed; + bool _isArrivalDone; + uint32 _pathId; + bool _repeating; + bool _loadedFromDB; + bool _stalled; }; -/** FlightPathMovementGenerator generates movement of the player for the paths +/** + * FlightPathMovementGenerator generates movement of the player for the paths * and hence generates ground and activities for the player. */ -class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, FlightPathMovementGenerator >, - public PathMovementBase<Player, TaxiPathNodeList> +class FlightPathMovementGenerator : public MovementGeneratorMedium<Player, FlightPathMovementGenerator>, public PathMovementBase<Player, std::vector<TaxiPathNodeEntry const*>> { public: - explicit FlightPathMovementGenerator() - { - i_currentNode = 0; - _endGridX = 0.0f; - _endGridY = 0.0f; - _endMapId = 0; - _preloadTargetNode = 0; - } + explicit FlightPathMovementGenerator(); void LoadPath(Player* player, uint32 startNode = 0); void DoInitialize(Player*); void DoReset(Player*); @@ -122,15 +108,14 @@ class FlightPathMovementGenerator : public MovementGeneratorMedium< Player, Flig bool DoUpdate(Player*, uint32); MovementGeneratorType GetMovementGeneratorType() const override { return FLIGHT_MOTION_TYPE; } - TaxiPathNodeList const& GetPath() { return i_path; } + std::vector<TaxiPathNodeEntry const*> const& GetPath() { return _path; } uint32 GetPathAtMapEnd() const; - bool HasArrived() const { return (i_currentNode >= i_path.size()); } + bool HasArrived() const { return (_currentNode >= _path.size()); } void SetCurrentNodeAfterTeleport(); - void SkipCurrentNode() { ++i_currentNode; } + void SkipCurrentNode() { ++_currentNode; } void DoEventIfAny(Player* player, TaxiPathNodeEntry const* node, bool departure); bool GetResetPos(Player*, float& x, float& y, float& z); - void InitEndGridInfo(); void PreloadEndGrid(); diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index e06e61c278d..2fb8e360de1 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -15,27 +15,12 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "WaypointManager.h" #include "DatabaseEnv.h" #include "GridDefines.h" -#include "WaypointManager.h" #include "MapManager.h" #include "Log.h" -WaypointMgr::WaypointMgr() { } - -WaypointMgr::~WaypointMgr() -{ - for (WaypointPathContainer::iterator itr = _waypointStore.begin(); itr != _waypointStore.end(); ++itr) - { - for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it) - delete *it; - - itr->second.clear(); - } - - _waypointStore.clear(); -} - void WaypointMgr::Load() { uint32 oldMSTime = getMSTime(); @@ -54,11 +39,7 @@ void WaypointMgr::Load() do { Field* fields = result->Fetch(); - WaypointData* wp = new WaypointData(); - uint32 pathId = fields[0].GetUInt32(); - WaypointPath& path = _waypointStore[pathId]; - float x = fields[2].GetFloat(); float y = fields[3].GetFloat(); float z = fields[4].GetFloat(); @@ -67,25 +48,27 @@ void WaypointMgr::Load() Trinity::NormalizeMapCoord(x); Trinity::NormalizeMapCoord(y); - wp->id = fields[1].GetUInt32(); - wp->x = x; - wp->y = y; - wp->z = z; - wp->orientation = o; - wp->move_type = fields[6].GetUInt32(); + WaypointNode waypoint; + waypoint.id = fields[1].GetUInt32(); + waypoint.x = x; + waypoint.y = y; + waypoint.z = z; + waypoint.orientation = o; + waypoint.moveType = fields[6].GetUInt32(); - if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.moveType >= WAYPOINT_MOVE_TYPE_MAX) { - TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id); - delete wp; + TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", waypoint.id); continue; } - wp->delay = fields[7].GetUInt32(); - wp->event_id = fields[8].GetUInt32(); - wp->event_chance = fields[9].GetInt16(); + waypoint.delay = fields[7].GetUInt32(); + waypoint.eventId = fields[8].GetUInt32(); + waypoint.eventChance = fields[9].GetInt16(); - path.push_back(wp); + WaypointPath& path = _waypointStore[pathId]; + path.id = pathId; + path.nodes.push_back(std::move(waypoint)); ++count; } while (result->NextRow()); @@ -101,14 +84,9 @@ WaypointMgr* WaypointMgr::instance() void WaypointMgr::ReloadPath(uint32 id) { - WaypointPathContainer::iterator itr = _waypointStore.find(id); + auto itr = _waypointStore.find(id); if (itr != _waypointStore.end()) - { - for (WaypointPath::const_iterator it = itr->second.begin(); it != itr->second.end(); ++it) - delete *it; - _waypointStore.erase(itr); - } WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID); @@ -119,13 +97,10 @@ void WaypointMgr::ReloadPath(uint32 id) if (!result) return; - WaypointPath& path = _waypointStore[id]; - + std::vector<WaypointNode> values; do { Field* fields = result->Fetch(); - WaypointData* wp = new WaypointData(); - float x = fields[1].GetFloat(); float y = fields[2].GetFloat(); float z = fields[3].GetFloat(); @@ -134,26 +109,36 @@ void WaypointMgr::ReloadPath(uint32 id) Trinity::NormalizeMapCoord(x); Trinity::NormalizeMapCoord(y); - wp->id = fields[0].GetUInt32(); - wp->x = x; - wp->y = y; - wp->z = z; - wp->orientation = o; - wp->move_type = fields[5].GetUInt32(); + WaypointNode waypoint; + waypoint.id = fields[0].GetUInt32(); + waypoint.x = x; + waypoint.y = y; + waypoint.z = z; + waypoint.orientation = o; + waypoint.moveType = fields[5].GetUInt32(); - if (wp->move_type >= WAYPOINT_MOVE_TYPE_MAX) + if (waypoint.moveType >= WAYPOINT_MOVE_TYPE_MAX) { - TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", wp->id); - delete wp; + TC_LOG_ERROR("sql.sql", "Waypoint %u in waypoint_data has invalid move_type, ignoring", waypoint.id); continue; } - wp->delay = fields[6].GetUInt32(); - wp->event_id = fields[7].GetUInt32(); - wp->event_chance = fields[8].GetUInt8(); - - path.push_back(wp); + waypoint.delay = fields[6].GetUInt32(); + waypoint.eventId = fields[7].GetUInt32(); + waypoint.eventChance = fields[8].GetUInt8(); + values.push_back(std::move(waypoint)); } while (result->NextRow()); + + _waypointStore[id] = WaypointPath(id, std::move(values)); +} + +WaypointPath const* WaypointMgr::GetPath(uint32 id) const +{ + auto itr = _waypointStore.find(id); + if (itr != _waypointStore.end()) + return &itr->second; + + return nullptr; } diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h index afff405dcd0..19e5caae826 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.h +++ b/src/server/game/Movement/Waypoints/WaypointManager.h @@ -19,32 +19,10 @@ #define TRINITY_WAYPOINTMANAGER_H #include "Define.h" +#include "WaypointDefines.h" #include <vector> #include <unordered_map> -enum WaypointMoveType -{ - WAYPOINT_MOVE_TYPE_WALK, - WAYPOINT_MOVE_TYPE_RUN, - WAYPOINT_MOVE_TYPE_LAND, - WAYPOINT_MOVE_TYPE_TAKEOFF, - - WAYPOINT_MOVE_TYPE_MAX -}; - -struct WaypointData -{ - uint32 id; - float x, y, z, orientation; - uint32 delay; - uint32 event_id; - uint32 move_type; - uint8 event_chance; -}; - -typedef std::vector<WaypointData*> WaypointPath; -typedef std::unordered_map<uint32, WaypointPath> WaypointPathContainer; - class TC_GAME_API WaypointMgr { public: @@ -57,20 +35,12 @@ class TC_GAME_API WaypointMgr void Load(); // Returns the path from a given id - WaypointPath const* GetPath(uint32 id) const - { - WaypointPathContainer::const_iterator itr = _waypointStore.find(id); - if (itr != _waypointStore.end()) - return &itr->second; - - return nullptr; - } + WaypointPath const* GetPath(uint32 id) const; private: - WaypointMgr(); - ~WaypointMgr(); + WaypointMgr() { } - WaypointPathContainer _waypointStore; + std::unordered_map<uint32, WaypointPath> _waypointStore; }; #define sWaypointMgr WaypointMgr::instance() diff --git a/src/server/game/Scripting/ScriptSystem.cpp b/src/server/game/Scripting/ScriptSystem.cpp index e2641447086..0cb051b3cdf 100644 --- a/src/server/game/Scripting/ScriptSystem.cpp +++ b/src/server/game/Scripting/ScriptSystem.cpp @@ -36,17 +36,17 @@ void SystemMgr::LoadScriptWaypoints() { uint32 oldMSTime = getMSTime(); - // Drop Existing Waypoint list - m_mPointMoveMap.clear(); + // drop Existing Waypoint list + _waypointStore.clear(); - uint64 uiCreatureCount = 0; + uint64 entryCount = 0; - // Load Waypoints + // load Waypoints QueryResult result = WorldDatabase.Query("SELECT COUNT(entry) FROM script_waypoint GROUP BY entry"); if (result) - uiCreatureCount = result->GetRowCount(); + entryCount = result->GetRowCount(); - TC_LOG_INFO("server.loading", "Loading Script Waypoints for " UI64FMTD " creature(s)...", uiCreatureCount); + TC_LOG_INFO("server.loading", "Loading Script Waypoints for " UI64FMTD " creature(s)...", entryCount); // 0 1 2 3 4 5 result = WorldDatabase.Query("SELECT entry, pointid, location_x, location_y, location_z, waittime FROM script_waypoint ORDER BY pointid"); @@ -60,29 +60,28 @@ void SystemMgr::LoadScriptWaypoints() do { - Field* pFields = result->Fetch(); - ScriptPointMove temp; - - temp.uiCreatureEntry = pFields[0].GetUInt32(); - uint32 uiEntry = temp.uiCreatureEntry; - temp.uiPointId = pFields[1].GetUInt32(); - temp.fX = pFields[2].GetFloat(); - temp.fY = pFields[3].GetFloat(); - temp.fZ = pFields[4].GetFloat(); - temp.uiWaitTime = pFields[5].GetUInt32(); - - CreatureTemplate const* pCInfo = sObjectMgr->GetCreatureTemplate(temp.uiCreatureEntry); - - if (!pCInfo) + Field* fields = result->Fetch(); + uint32 entry = fields[0].GetUInt32(); + uint32 id = fields[1].GetUInt32(); + float x = fields[2].GetFloat(); + float y = fields[3].GetFloat(); + float z = fields[4].GetFloat(); + uint32 waitTime = fields[5].GetUInt32(); + + CreatureTemplate const* info = sObjectMgr->GetCreatureTemplate(entry); + if (!info) { - TC_LOG_ERROR("sql.sql", "TSCR: DB table script_waypoint has waypoint for non-existant creature entry %u", temp.uiCreatureEntry); + TC_LOG_ERROR("sql.sql", "SystemMgr: DB table script_waypoint has waypoint for non-existant creature entry %u", entry); continue; } - if (!pCInfo->ScriptID) - TC_LOG_ERROR("sql.sql", "TSCR: DB table script_waypoint has waypoint for creature entry %u, but creature does not have ScriptName defined and then useless.", temp.uiCreatureEntry); + if (!info->ScriptID) + TC_LOG_ERROR("sql.sql", "SystemMgr: DB table script_waypoint has waypoint for creature entry %u, but creature does not have ScriptName defined and then useless.", entry); + + WaypointPath& path = _waypointStore[entry]; + path.id = entry; + path.nodes.emplace_back(id, x, y, z, 0.f, waitTime); - m_mPointMoveMap[uiEntry].push_back(temp); ++count; } while (result->NextRow()); @@ -162,6 +161,15 @@ void SystemMgr::LoadScriptSplineChains() } } +WaypointPath const* SystemMgr::GetPath(uint32 creatureEntry) const +{ + auto itr = _waypointStore.find(creatureEntry); + if (itr == _waypointStore.end()) + return nullptr; + + return &itr->second; +} + std::vector<SplineChainLink> const* SystemMgr::GetSplineChain(uint32 entry, uint16 chainId) const { auto it = m_mSplineChainsMap.find({ entry, chainId }); diff --git a/src/server/game/Scripting/ScriptSystem.h b/src/server/game/Scripting/ScriptSystem.h index f058bf886ee..772b99244f8 100644 --- a/src/server/game/Scripting/ScriptSystem.h +++ b/src/server/game/Scripting/ScriptSystem.h @@ -20,59 +20,39 @@ #include "Define.h" #include "Hash.h" +#include "WaypointDefines.h" #include <unordered_map> #include <vector> class Creature; struct SplineChainLink; -#define TEXT_SOURCE_RANGE -1000000 //the amount of entries each text source has available - -struct ScriptPointMove -{ - uint32 uiCreatureEntry; - uint32 uiPointId; - float fX; - float fY; - float fZ; - uint32 uiWaitTime; -}; - -typedef std::vector<ScriptPointMove> ScriptPointVector; +#define TEXT_SOURCE_RANGE -1000000 // the amount of entries each text source has available class TC_GAME_API SystemMgr { - private: - SystemMgr(); - ~SystemMgr(); - SystemMgr(SystemMgr const&) = delete; - SystemMgr& operator=(SystemMgr const&) = delete; - public: static SystemMgr* instance(); - typedef std::unordered_map<uint32, ScriptPointVector> PointMoveMap; - - //Database + // database void LoadScriptWaypoints(); void LoadScriptSplineChains(); - ScriptPointVector const* GetPointMoveList(uint32 creatureEntry) const - { - PointMoveMap::const_iterator itr = m_mPointMoveMap.find(creatureEntry); - - if (itr == m_mPointMoveMap.end()) - return nullptr; - - return &itr->second; - } + WaypointPath const* GetPath(uint32 creatureEntry) const; std::vector<SplineChainLink> const* GetSplineChain(uint32 entry, uint16 chainId) const; std::vector<SplineChainLink> const* GetSplineChain(Creature const* who, uint16 id) const; - protected: - PointMoveMap m_mPointMoveMap; //coordinates for waypoints + private: typedef std::pair<uint32, uint16> ChainKeyType; // creature entry + chain ID + + SystemMgr(); + ~SystemMgr(); + + SystemMgr(SystemMgr const&) = delete; + SystemMgr& operator=(SystemMgr const&) = delete; + + std::unordered_map<uint32, WaypointPath> _waypointStore; std::unordered_map<ChainKeyType, std::vector<SplineChainLink>> m_mSplineChainsMap; // spline chains }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 61e59d53641..e7335bb766f 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -92,7 +92,7 @@ #include "VMapFactory.h" #include "VMapManager2.h" #include "WardenCheckMgr.h" -#include "WaypointMovementGenerator.h" +#include "WaypointManager.h" #include "WeatherMgr.h" #include "WhoListStorage.h" #include "WorldSession.h" |
