diff --git a/doc/EventAI.txt b/doc/EventAI.txt index 2724977a75..8dd2d1d729 100644 --- a/doc/EventAI.txt +++ b/doc/EventAI.txt @@ -161,7 +161,7 @@ For all ACTION_T_RANDOM Actions, When a Particular Param is selected for the Eve 45 ACTION_T_THROW_AI_EVENT EventType, Radius, Target Throws an AIEvent of type (Param1) to nearby friendly Npcs in range of (Param2), Invoker of event is Target 46 ACTION_T_SET_THROW_MASK EventTypeMask Marks for which AIEvents the npc will throw AIEvents on its own. 47 ACTION_T_SET_STAND_STATE StandState Set the unit stand state (Param1) of the current creature. -48 ACTION_T_CHANGE_MOVEMENT MovementType, WanderDistance Change the unit movement type (Param1). If the movement type is Random Movement (1), the WanderDistance (Param2) must be provided. If the movement type is Waypoint Movement (2), Param2 is PathId. Param3 is asDefault. +48 ACTION_T_CHANGE_MOVEMENT MovementType, WanderDistance Change the unit movement type (Param1). If the movement type is Random Movement (1), the WanderDistance (Param2) must be provided. If the movement type is Waypoint Movement (2), Param2 is PathId. Param3 0x1 is asDefault, 0x2 forces waypoint, path or linear path movement to use waypoint_path table PathId 49 ACTION_T_REUSE 50 ACTION_T_SET_REACT_STATE ReactState Change react state of the creature 51 ACTION_T_PAUSE_WAYPOINTS DoPause Pause waypoints of creature diff --git a/doc/script_commands.txt b/doc/script_commands.txt index c94f59eaa1..5ebe5e0411 100644 --- a/doc/script_commands.txt +++ b/doc/script_commands.txt @@ -260,9 +260,10 @@ Defining a buddy could be done in several way: * datalog = movie id 20 SCRIPT_COMMAND_MOVEMENT resultingSource = Creature - * datalong = MovementType (0:idle, 1:random, 2:waypoint, 3:path) + * datalong = MovementType (0:idle, 1:random, 2:waypoint, 3:path, 4:linear) * datalong2 = wanderDistance (for random movement), pathId (for waypoint movement) - * datalong3 = timer for timed random movement or pass target to waypoint and path movegen (by default, source targets self) + * datalong3 = 0x1 - timer for timed random movement or pass target to waypoint and path movegen (by default, source targets self) + * 0x2 - for waypoint, path and linear path - uses pathId from waypoint_path table * data_flags & SCRIPT_FLAG_COMMAND_ADDITIONAL: RandomMovement around current position * dataint = enum ForcedMovement diff --git a/src/game/AI/EventAI/CreatureEventAI.cpp b/src/game/AI/EventAI/CreatureEventAI.cpp index 1b2e25491e..2ad9489c86 100644 --- a/src/game/AI/EventAI/CreatureEventAI.cpp +++ b/src/game/AI/EventAI/CreatureEventAI.cpp @@ -1189,7 +1189,7 @@ bool CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 } case ACTION_T_CHANGE_MOVEMENT: { - if (action.changeMovement.asDefault) + if (action.changeMovement.flags & CHANGE_MOVEMENT_FLAG_AS_DEFAULT) m_defaultMovement = MovementGeneratorType(action.changeMovement.movementType); switch (action.changeMovement.movementType) { @@ -1200,15 +1200,34 @@ bool CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 m_creature->GetMotionMaster()->MoveRandomAroundPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), float(action.changeMovement.wanderORpathID)); break; case WAYPOINT_MOTION_TYPE: + { m_creature->StopMoving(); m_creature->GetMotionMaster()->Clear(false, true); - m_creature->GetMotionMaster()->MoveWaypoint(action.changeMovement.wanderORpathID); + WaypointPathOrigin origin = PATH_NO_PATH; + if (action.changeMovement.flags & CHANGE_MOVEMENT_FLAG_WAYPOINT_PATH) + origin = PATH_FROM_WAYPOINT_PATH; + m_creature->GetMotionMaster()->MoveWaypoint(action.changeMovement.wanderORpathID, origin); + break; + } + case PATH_MOTION_TYPE: + { + m_creature->StopMoving(); + WaypointPathOrigin origin = PATH_NO_PATH; + if (action.changeMovement.flags & CHANGE_MOVEMENT_FLAG_WAYPOINT_PATH) + origin = PATH_FROM_WAYPOINT_PATH; + m_creature->GetMotionMaster()->MovePath(action.changeMovement.wanderORpathID, origin); break; + } case LINEAR_WP_MOTION_TYPE: + { m_creature->StopMoving(); m_creature->GetMotionMaster()->Clear(false, true); - m_creature->GetMotionMaster()->MoveLinearWP(action.changeMovement.wanderORpathID); + WaypointPathOrigin origin = PATH_NO_PATH; + if (action.changeMovement.flags & CHANGE_MOVEMENT_FLAG_WAYPOINT_PATH) + origin = PATH_FROM_WAYPOINT_PATH; + m_creature->GetMotionMaster()->MoveLinearWP(action.changeMovement.wanderORpathID, origin); break; + } } break; } diff --git a/src/game/AI/EventAI/CreatureEventAI.h b/src/game/AI/EventAI/CreatureEventAI.h index 9be6d65eca..c85b0a7fd4 100644 --- a/src/game/AI/EventAI/CreatureEventAI.h +++ b/src/game/AI/EventAI/CreatureEventAI.h @@ -229,6 +229,13 @@ enum DespawnAggregation : uint32 AGGREGATION_DEATH = 0x4, }; +enum ChangeMovementFlags : uint32 +{ + CHANGE_MOVEMENT_FLAG_AS_DEFAULT = 0x1, + CHANGE_MOVEMENT_FLAG_WAYPOINT_PATH = 0x2, + CHANGE_MOVEMENT_MAX = 0x3, +}; + struct CreatureEventAI_Action { EventAI_ActionType type: 16; @@ -480,7 +487,7 @@ struct CreatureEventAI_Action { uint32 movementType; uint32 wanderORpathID; - uint32 asDefault; + uint32 flags; } changeMovement; // ACTION_T_REUSE = 49 struct diff --git a/src/game/AI/EventAI/CreatureEventAIMgr.cpp b/src/game/AI/EventAI/CreatureEventAIMgr.cpp index dfd40a59f5..0eadb99eac 100644 --- a/src/game/AI/EventAI/CreatureEventAIMgr.cpp +++ b/src/game/AI/EventAI/CreatureEventAIMgr.cpp @@ -903,10 +903,10 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() { sLog.outErrorEventAI("Event %u Action %u uses invalid movement type %u (must be smaller than %u)", eventId, j + 1, action.changeMovement.movementType, MAX_DB_MOTION_TYPE); } - if (action.changeMovement.asDefault > 1) + if (action.changeMovement.flags > CHANGE_MOVEMENT_MAX) { - sLog.outErrorEventAI("Event %u Action %u uses invalid default movement setting %u. Setting to 0.", eventId, j + 1, action.changeMovement.asDefault); - action.changeMovement.asDefault = 0; + sLog.outErrorEventAI("Event %u Action %u uses invalid flags setting %u. Setting to 0.", eventId, j + 1, action.changeMovement.flags); + action.changeMovement.flags = 0; } break; case ACTION_T_SET_REACT_STATE: diff --git a/src/game/AI/ScriptDevAI/base/escort_ai.cpp b/src/game/AI/ScriptDevAI/base/escort_ai.cpp index caf5406ce3..baf165bf04 100644 --- a/src/game/AI/ScriptDevAI/base/escort_ai.cpp +++ b/src/game/AI/ScriptDevAI/base/escort_ai.cpp @@ -23,7 +23,8 @@ npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature), m_isRunning(false), m_canInstantRespawn(false), m_canReturnToStart(false), - m_waypointPathID(0) + m_waypointPathID(0), + m_currentEscortWaypointPath(0) {} void npc_escortAI::GetAIInformation(ChatHandler& reader) @@ -232,7 +233,7 @@ void npc_escortAI::SetRun(bool run) } // TODO: get rid of this many variables passed in function. -void npc_escortAI::Start(bool run, const Player* player, const Quest* quest, bool instantRespawn, bool canLoopPath) +void npc_escortAI::Start(bool run, const Player* player, const Quest* quest, bool instantRespawn, bool canLoopPath, uint32 waypointPath) { if (m_creature->GetVictim()) { @@ -246,12 +247,23 @@ void npc_escortAI::Start(bool run, const Player* player, const Quest* quest, boo return; } - if (!sWaypointMgr.GetPathFromOrigin(m_creature->GetEntry(), m_creature->GetGUIDLow(), m_waypointPathID, PATH_FROM_EXTERNAL)) + uint32 pathId = m_waypointPathID; + WaypointPathOrigin origin = PATH_FROM_EXTERNAL; + if (waypointPath) + { + pathId = waypointPath; + origin = PATH_FROM_WAYPOINT_PATH; + } + + if (!sWaypointMgr.GetPathFromOrigin(m_creature->GetEntry(), m_creature->GetGUIDLow(), pathId, origin)) { script_error_log("EscortAI attempt to start escorting for %s, but has no waypoints loaded.", m_creature->GetScriptName().data()); return; } + if (origin == PATH_FROM_WAYPOINT_PATH) + m_currentEscortWaypointPath = pathId; + // set variables m_isRunning = run; @@ -276,7 +288,7 @@ void npc_escortAI::Start(bool run, const Player* player, const Quest* quest, boo // Start moving along the path with 2500ms delay m_creature->GetMotionMaster()->Clear(false, true); - m_creature->GetMotionMaster()->MoveWaypoint(m_waypointPathID, PATH_FROM_EXTERNAL, 2500); + m_creature->GetMotionMaster()->MoveWaypoint(pathId, origin, 2500); JustStartedEscort(); } diff --git a/src/game/AI/ScriptDevAI/base/escort_ai.h b/src/game/AI/ScriptDevAI/base/escort_ai.h index 020caa75a0..312dd0411f 100644 --- a/src/game/AI/ScriptDevAI/base/escort_ai.h +++ b/src/game/AI/ScriptDevAI/base/escort_ai.h @@ -39,7 +39,7 @@ struct npc_escortAI : public ScriptedAI virtual void WaypointReached(uint32 pointId) = 0; virtual void WaypointStart(uint32 /*pointId*/) {} - void Start(bool run = false, const Player* player = nullptr, const Quest* quest = nullptr, bool instantRespawn = false, bool canLoopPath = false); + void Start(bool run = false, const Player* player = nullptr, const Quest* quest = nullptr, bool instantRespawn = false, bool canLoopPath = false, uint32 waypointPath = 0); void SetRun(bool run = true); void SetEscortPaused(bool paused); @@ -52,6 +52,8 @@ struct npc_escortAI : public ScriptedAI void FailQuestForPlayerAndGroup(); bool AssistPlayerInCombat(Unit* who) override; + + uint32 GetCurrentWaypointPath() const { return m_currentEscortWaypointPath; } protected: Player* GetPlayerForEscort() const { return m_creature->GetMap()->GetPlayer(m_playerGuid); } bool IsSD2EscortMovement(uint32 moveType) const; @@ -75,6 +77,7 @@ struct npc_escortAI : public ScriptedAI bool m_canReturnToStart; // if creature can walk same path (loop) without despawn. Not for regular escort quests. uint32 m_waypointPathID; + uint32 m_currentEscortWaypointPath; }; #endif diff --git a/src/game/DBScripts/ScriptMgr.cpp b/src/game/DBScripts/ScriptMgr.cpp index 7d63da5e38..aaea9c69cf 100644 --- a/src/game/DBScripts/ScriptMgr.cpp +++ b/src/game/DBScripts/ScriptMgr.cpp @@ -2047,7 +2047,7 @@ bool ScriptAction::ExecuteDbscriptCommand(WorldObject* pSource, WorldObject* pTa if (m_script->movement.movementType == WAYPOINT_MOTION_TYPE || m_script->movement.movementType == PATH_MOTION_TYPE) { - if (m_script->movement.timerOrPassTarget && !pTarget) + if ((m_script->movement.timerOrPassTarget & 0x1) && !pTarget) { DETAIL_FILTER_LOG(LOG_FILTER_DB_SCRIPT, " DB-SCRIPTS: Process table `%s` id %u, SCRIPT_COMMAND_MOVEMENT called for movement change to %u with source guid %s, pass target true and target nullptr: skipping.", m_table, m_script->id, m_script->movement.movementType, pSource->GetGuidStr().c_str()); break; @@ -2073,28 +2073,43 @@ bool ScriptAction::ExecuteDbscriptCommand(WorldObject* pSource, WorldObject* pTa } break; case WAYPOINT_MOTION_TYPE: + { source->StopMoving(); source->GetMotionMaster()->Clear(false, true); - if (!m_script->movement.timerOrPassTarget) + WaypointPathOrigin origin = PATH_NO_PATH; + if (m_script->movement.timerOrPassTarget & 0x2) + origin = PATH_FROM_WAYPOINT_PATH; + if (!m_script->movement.timerOrPassTarget & 0x1) source->GetMotionMaster()->MoveWaypoint(m_script->movement.wanderORpathId); else source->GetMotionMaster()->MoveWaypoint(m_script->movement.wanderORpathId, 0, 0, 0, ForcedMovement(m_script->textId[0]), pTarget->GetObjectGuid()); break; + } case PATH_MOTION_TYPE: + { source->StopMoving(); - if (!m_script->movement.timerOrPassTarget) + WaypointPathOrigin origin = PATH_NO_PATH; + if (m_script->movement.timerOrPassTarget & 0x2) + origin = PATH_FROM_WAYPOINT_PATH; + if (!m_script->movement.timerOrPassTarget & 0x1) source->GetMotionMaster()->MovePath(m_script->movement.wanderORpathId); else source->GetMotionMaster()->MovePath(m_script->movement.wanderORpathId, PATH_NO_PATH, ForcedMovement(m_script->textId[0]), false, 0.f, false, pTarget->GetObjectGuid()); break; + } case LINEAR_WP_MOTION_TYPE: + { source->StopMoving(); source->GetMotionMaster()->Clear(false, true); - if (!m_script->movement.timerOrPassTarget) + WaypointPathOrigin origin = PATH_NO_PATH; + if (m_script->movement.timerOrPassTarget & 0x2) + origin = PATH_FROM_WAYPOINT_PATH; + if (!m_script->movement.timerOrPassTarget & 0x1) source->GetMotionMaster()->MoveLinearWP(m_script->movement.wanderORpathId); else source->GetMotionMaster()->MoveLinearWP(m_script->movement.wanderORpathId, 0, 0, 0, ForcedMovement(m_script->textId[0]), pTarget->GetObjectGuid()); break; + } } break; diff --git a/src/game/MotionGenerators/WaypointMovementGenerator.cpp b/src/game/MotionGenerators/WaypointMovementGenerator.cpp index 4355ded358..3dc6befdcf 100644 --- a/src/game/MotionGenerators/WaypointMovementGenerator.cpp +++ b/src/game/MotionGenerators/WaypointMovementGenerator.cpp @@ -28,6 +28,7 @@ #include "Movement/MoveSpline.h" #include "Maps/GridDefines.h" #include "Entities/Transports.h" +#include "AI/ScriptDevAI/base/escort_ai.h" #include @@ -189,8 +190,18 @@ void WaypointMovementGenerator::OnArrived(Creature& creature) // Inform script if (creature.AI()) { - uint32 type = WAYPOINT_MOTION_TYPE; + bool inform = false; if (m_PathOrigin == PATH_FROM_EXTERNAL) + inform = true; + else if (m_PathOrigin == PATH_FROM_WAYPOINT_PATH) // temporary transitional code for transitioning from script_texts to waypoint_path + { + if (npc_escortAI* ai = dynamic_cast(creature.AI())) + if (ai->GetCurrentWaypointPath() == m_pathId) + inform = true; + } + + uint32 type = WAYPOINT_MOTION_TYPE; + if (inform) type = EXTERNAL_WAYPOINT_MOVE; InformAI(creature, type, i_currentNode); } @@ -226,8 +237,18 @@ void WaypointMovementGenerator::OnArrived(Creature& creature) // Fire event only if it is already moving to the next node (for normal start it is already done in SendNewPath) if (!m_nodeIndexes.empty() && !Stopped(creature)) { + bool inform = false; + if (m_PathOrigin == PATH_FROM_EXTERNAL) + inform = true; + else if (m_PathOrigin == PATH_FROM_WAYPOINT_PATH) // temporary transitional code for transitioning from script_texts to waypoint_path + { + if (npc_escortAI* ai = dynamic_cast(creature.AI())) + if (ai->GetCurrentWaypointPath() == m_pathId) + inform = true; + } + // Inform AI that we start to move - if (creature.AI() && m_PathOrigin == PATH_FROM_EXTERNAL) + if (creature.AI() && inform) InformAI(creature, uint32(EXTERNAL_WAYPOINT_MOVE_START), m_currentWaypointNode->first); } @@ -307,8 +328,18 @@ void WaypointMovementGenerator::SendNextWayPointPath(Creature& creatur WaypointNode const* nextNode = &m_currentWaypointNode->second; + bool inform = false; + if (m_PathOrigin == PATH_FROM_EXTERNAL) + inform = true; + else if (m_PathOrigin == PATH_FROM_WAYPOINT_PATH) // temporary transitional code for transitioning from script_texts to waypoint_path + { + if (npc_escortAI* ai = dynamic_cast(creature.AI())) + if (ai->GetCurrentWaypointPath() == m_pathId) + inform = true; + } + // Inform AI that we start to move or reached last node - if (creature.AI() && m_PathOrigin == PATH_FROM_EXTERNAL) + if (creature.AI() && inform) { uint32 type = uint32(EXTERNAL_WAYPOINT_MOVE_START); if (i_path->begin()->first == i_currentNode && i_path->rbegin()->first == m_lastReachedWaypoint)