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/AI/ScriptedAI | |
| 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/AI/ScriptedAI')
| -rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp | 521 | ||||
| -rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedEscortAI.h | 132 | ||||
| -rw-r--r-- | src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h | 2 |
3 files changed, 238 insertions, 417 deletions
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; |
