fix(Core/Unit): rework Walk/Run mode (#22988)

Co-authored-by: sudlud <sudlud@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
killerwife
2025-10-14 22:54:19 +02:00
committed by GitHub
parent c28f635408
commit 10d5a3c553
104 changed files with 373 additions and 362 deletions

View File

@@ -552,6 +552,11 @@ void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO
}
}
void ScriptedAI::SetRun(bool run)
{
me->SetWalk(!run);
}
enum eNPCs
{
NPC_BROODLORD = 12017,

View File

@@ -227,6 +227,8 @@ struct ScriptedAI : public CreatureAI
// Called when AI is temporarily replaced or put back when possess is applied or removed
void OnPossess(bool /*apply*/) {}
void SetRun(bool run);
enum class Axis
{
AXIS_X,

View File

@@ -33,7 +33,6 @@ npc_escortAI::npc_escortAI(Creature* creature) : ScriptedAI(creature),
MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
m_pQuestForEscort(nullptr),
m_bIsActiveAttacker(true),
m_bIsRunning(false),
m_bCanInstantRespawn(false),
m_bCanReturnToStart(false),
DespawnAtEnd(true),
@@ -186,9 +185,8 @@ void npc_escortAI::JustRespawned()
void npc_escortAI::ReturnToLastPoint()
{
float x, y, z, o;
me->SetWalk(false);
me->GetHomePosition(x, y, z, o);
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z, FORCED_MOVEMENT_RUN);
}
void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
@@ -329,7 +327,6 @@ void npc_escortAI::MovementInform(uint32 moveType, uint32 pointId)
{
LOG_DEBUG("scripts.ai", "EscortAI has returned to original position before combat");
me->SetWalk(!m_bIsRunning);
RemoveEscortState(STATE_ESCORT_RETURNING);
if (!m_uiWPWaitTimer)
@@ -418,28 +415,8 @@ void npc_escortAI::FillPointMovementListForCreature()
}
}
void npc_escortAI::SetRun(bool on)
{
if (on)
{
if (!m_bIsRunning)
me->SetWalk(false);
else
LOG_DEBUG("scripts.ai", "EscortAI attempt to set run mode, but is already running.");
}
else
{
if (m_bIsRunning)
me->SetWalk(true);
else
LOG_DEBUG("scripts.ai", "EscortAI attempt to set walk mode, but is already walking.");
}
m_bIsRunning = on;
}
//TODO: get rid of this many variables passed in function.
void npc_escortAI::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 npc_escortAI::Start(bool isActiveAttacker /* = true*/, ObjectGuid playerGUID /* = ObjectGuid::Empty */, Quest const* quest /* = nullptr */, bool instantRespawn /* = false */, bool canLoopPath /* = false */, bool resetWaypoints /* = true */)
{
if (me->GetVictim())
{
@@ -469,7 +446,6 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false
//set variables
m_bIsActiveAttacker = isActiveAttacker;
m_bIsRunning = run;
m_uiPlayerGUID = playerGUID;
m_pQuestForEscort = quest;
@@ -495,17 +471,11 @@ void npc_escortAI::Start(bool isActiveAttacker /* = true*/, bool run /* = false
me->SetImmuneToNPC(false);
}
LOG_DEBUG("scripts.ai", "EscortAI started with {} waypoints. ActiveAttacker = {}, Run = {}, PlayerGUID = {}",
uint64(WaypointList.size()), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID.ToString());
LOG_DEBUG("scripts.ai", "EscortAI started with {} waypoints. ActiveAttacker = {}, PlayerGUID = {}",
uint64(WaypointList.size()), m_bIsActiveAttacker, m_uiPlayerGUID.ToString());
CurrentWP = WaypointList.begin();
//Set initial speed
if (m_bIsRunning)
me->SetWalk(false);
else
me->SetWalk(true);
AddEscortState(STATE_ESCORT_ESCORTING);
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == ESCORT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();

View File

@@ -90,9 +90,8 @@ public:
virtual void WaypointReached(uint32 pointId) = 0;
virtual void WaypointStart(uint32 /*pointId*/) {}
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 Start(bool isActiveAttacker = true, 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);
bool HasEscortState(uint32 escortState) { return (m_uiEscortState & escortState); }
@@ -130,7 +129,6 @@ private:
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;

View File

@@ -43,9 +43,6 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
mCanRepeatPath = false;
// spawn in run mode
// Xinef: spawn in run mode and set mRun to run... this overrides SetWalk EVERYWHERE
mRun = true;
mEvadeDisabled = false;
mCanAutoAttack = true;
@@ -190,7 +187,7 @@ void SmartAI::GenerateWayPointArray(Movement::PointsArray* points)
}
}
void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
void SmartAI::StartPath(ForcedMovement forcedMovement, uint32 path, bool repeat, Unit* invoker)
{
if (HasEscortState(SMART_ESCORT_ESCORTING))
StopPath();
@@ -208,7 +205,6 @@ void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
{
AddEscortState(SMART_ESCORT_ESCORTING);
mCanRepeatPath = repeat;
SetRun(run);
if (invoker && invoker->IsPlayer())
{
@@ -219,7 +215,7 @@ void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
Movement::PointsArray pathPoints;
GenerateWayPointArray(&pathPoints);
me->GetMotionMaster()->MoveSplinePath(&pathPoints);
me->GetMotionMaster()->MoveSplinePath(&pathPoints, forcedMovement);
GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, wp->id, GetScript()->GetPathId());
}
}
@@ -256,7 +252,6 @@ void SmartAI::PausePath(uint32 delay, bool forced)
if (forced && !mWPReached)
{
mForcedPaused = forced;
SetRun(mRun);
if (me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == ESCORT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
@@ -362,7 +357,7 @@ void SmartAI::EndPath(bool fail)
if (mCanRepeatPath)
{
if (IsAIControlled())
StartPath(mRun, GetScript()->GetPathId(), true);
StartPath(FORCED_MOVEMENT_NONE, GetScript()->GetPathId(), true);
}
else
GetScript()->SetPathId(0);
@@ -373,8 +368,6 @@ void SmartAI::EndPath(bool fail)
void SmartAI::ResumePath()
{
SetRun(mRun);
if (mLastWP)
{
Movement::PointsArray pathPoints;
@@ -389,10 +382,9 @@ void SmartAI::ReturnToLastOOCPos()
if (!IsAIControlled())
return;
me->SetWalk(false);
float x, y, z, o;
me->GetHomePosition(x, y, z, o);
me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, x, y, z);
me->GetMotionMaster()->MovePoint(SMART_ESCORT_LAST_OOC_POINT, x, y, z, FORCED_MOVEMENT_RUN);
}
void SmartAI::UpdatePath(const uint32 diff)
@@ -469,7 +461,6 @@ void SmartAI::UpdatePath(const uint32 diff)
EndPath();
else if (GetNextWayPoint())
{
SetRun(mRun);
// xinef: if we have reached waypoint, and there is no working spline movement it means our splitted array has ended, make new one
if (me->movespline->Finalized())
ResumePath();
@@ -629,7 +620,6 @@ void SmartAI::MovepointReached(uint32 id)
EndPath();
else if (GetNextWayPoint())
{
SetRun(mRun);
// xinef: if we have reached waypoint, and there is no working spline movement it means our splitted array has ended, make new one
if (me->movespline->Finalized())
ResumePath();
@@ -675,7 +665,6 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
GetScript()->ProcessEventsFor(SMART_EVENT_EVADE); //must be after aura clear so we can cast spells from db
SetRun(mRun);
if (HasEscortState(SMART_ESCORT_ESCORTING))
{
AddEscortState(SMART_ESCORT_RETURNING);
@@ -850,7 +839,6 @@ void SmartAI::AttackStart(Unit* who)
{
if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
{
SetRun(mRun);
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
if (type == ESCORT_MOTION_TYPE || type == POINT_MOTION_TYPE)
{
@@ -952,9 +940,7 @@ void SmartAI::OnCharmed(bool /* apply */)
if (!charmed && !me->IsInEvadeMode())
{
if (mCanRepeatPath)
StartPath(mRun, GetScript()->GetPathId(), true);
else
me->SetWalk(!mRun);
StartPath(FORCED_MOVEMENT_NONE, GetScript()->GetPathId(), true);
if (Unit* charmer = me->GetCharmer())
AttackStart(charmer);
@@ -1002,12 +988,6 @@ ObjectGuid SmartAI::GetGUID(int32 /*id*/) const
return ObjectGuid::Empty;
}
void SmartAI::SetRun(bool run)
{
me->SetWalk(!run);
mRun = run;
}
void SmartAI::SetFly(bool fly)
{
// xinef: set proper flag!
@@ -1108,7 +1088,6 @@ void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, ui
mFollowArrivedEntry = end;
mFollowArrivedAlive = !aliveState; // negate - 0 is alive
mFollowCreditType = creditType;
SetRun(mRun);
me->GetMotionMaster()->MoveFollow(target, mFollowDist, mFollowAngle);
}

View File

@@ -52,7 +52,7 @@ public:
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(ForcedMovement forcedMovement = FORCED_MOVEMENT_NONE, uint32 path = 0, bool repeat = false, Unit* invoker = nullptr);
bool LoadPath(uint32 entry);
void PausePath(uint32 delay, bool forced = false);
void StopPath(uint32 DespawnTime = 0, uint32 quest = 0, bool fail = false);
@@ -175,9 +175,6 @@ public:
// Called at movepoint reached
void MovepointReached(uint32 id);
// Makes the creature run/walk
void SetRun(bool run = true);
void SetFly(bool fly = true);
void SetSwim(bool swim = true);
@@ -240,7 +237,6 @@ private:
uint32 mEscortNPCFlags;
uint32 GetWPCount() { return mWayPoints ? mWayPoints->size() : 0; }
bool mCanRepeatPath;
bool mRun;
bool mEvadeDisabled;
bool mCanAutoAttack;
bool mForcedPaused;

View File

@@ -1683,10 +1683,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (IsCreature(target))
{
if (IsSmart(target->ToCreature()))
CAST_AI(SmartAI, target->ToCreature()->AI())->SetRun(e.action.setRun.run);
else
target->ToCreature()->SetWalk(e.action.setRun.run ? false : true); // Xinef: reversed
target->ToCreature()->SetWalk(e.action.setRun.run ? false : true);
}
}
@@ -1731,7 +1728,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!IsSmart())
break;
bool run = e.action.wpStart.run != 0;
ForcedMovement forcedMovement = static_cast<ForcedMovement>(e.action.wpStart.forcedMovement);
uint32 entry = e.action.wpStart.pathID;
bool repeat = e.action.wpStart.repeat != 0;
@@ -1745,7 +1742,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
me->SetReactState((ReactStates)e.action.wpStart.reactState);
CAST_AI(SmartAI, me->AI())->StartPath(run, entry, repeat, unit);
CAST_AI(SmartAI, me->AI())->StartPath(forcedMovement, entry, repeat, unit);
uint32 quest = e.action.wpStart.quest;
uint32 DespawnTime = e.action.wpStart.despawnTime;
@@ -1854,8 +1851,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (TransportBase* trans = me->GetDirectTransport())
trans->CalculatePassengerPosition(dest.x, dest.y, dest.z);
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, dest.x, dest.y, dest.z, true, isForced,
isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE, e.target.o);
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, dest.x, dest.y, dest.z, FORCED_MOVEMENT_NONE,
0.f, e.target.o, true, isForced, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE);
break;
}
@@ -1871,9 +1868,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
randomPoint.m_positionX,
randomPoint.m_positionY,
randomPoint.m_positionZ,
true,
isForced,
isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE
FORCED_MOVEMENT_NONE,
0.f, 0.f, true, isForced, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE
);
}
@@ -1897,7 +1893,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
else if (e.action.moveToPos.ContactDistance)
target->GetNearPoint(me, x, y, z, e.action.moveToPos.ContactDistance, 0, target->GetAngle(me));
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, x + e.target.x, y + e.target.y, z + e.target.z, true, isForced, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE);
me->GetMotionMaster()->MovePoint(e.action.moveToPos.pointId, x + e.target.x, y + e.target.y, z + e.target.z, FORCED_MOVEMENT_NONE,
0.f, 0.f, true, isForced, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE);
break;
}
@@ -1914,7 +1911,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
SAIBool isForced = !e.action.moveToPosTarget.disableForceDestination;
Creature* ctarget = target->ToCreature();
ctarget->GetMotionMaster()->MovePoint(e.action.moveToPosTarget.pointId, e.target.x, e.target.y, e.target.z, true, isForced, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE);
ctarget->GetMotionMaster()->MovePoint(e.action.moveToPosTarget.pointId, e.target.x, e.target.y, e.target.z, FORCED_MOVEMENT_NONE,
0.f, 0.f, true, isForced, isControlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE);
}
}
@@ -2543,9 +2541,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (closestWpId)
{
bool repeat = e.action.startClosestWaypoint.repeat;
bool run = e.action.startClosestWaypoint.run;
ForcedMovement forcedMovement = static_cast<ForcedMovement>(e.action.startClosestWaypoint.forcedMovement);
CAST_AI(SmartAI, creature->AI())->StartPath(repeat, closestWpId, run);
CAST_AI(SmartAI, creature->AI())->StartPath(forcedMovement, closestWpId, repeat);
}
}
}

View File

@@ -1564,11 +1564,11 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
return false;
}
if (e.action.startClosestWaypoint.repeat > 1 || e.action.startClosestWaypoint.run > 1)
if (e.action.startClosestWaypoint.repeat > 1 || e.action.startClosestWaypoint.forcedMovement >= FORCED_MOVEMENT_MAX)
{
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has invalid run ({}) or repeat ({}) parameter, must be 0 or 1.",
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has invalid forcedMovement ({}) or repeat ({}) parameter, must be 0 or 1.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(),
e.action.startClosestWaypoint.repeat, e.action.startClosestWaypoint.run);
e.action.startClosestWaypoint.repeat, e.action.startClosestWaypoint.forcedMovement);
return false;
}
break;
@@ -1745,8 +1745,13 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
return false;
}
return IsSAIBoolValid(e, e.action.wpStart.run) &&
IsSAIBoolValid(e, e.action.wpStart.repeat);
if (e.action.wpStart.forcedMovement >= FORCED_MOVEMENT_MAX)
{
LOG_ERROR("sql.sql", "SmartAIMgr: Creature {} Event {} Action {} uses invalid forcedMovement {}, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.forcedMovement);
return false;
}
return IsSAIBoolValid(e, e.action.wpStart.repeat);
}
case SMART_ACTION_CREATE_TIMED_EVENT:
{

View File

@@ -1041,7 +1041,7 @@ struct SmartAction
struct
{
SAIBool run;
uint32 forcedMovement;
uint32 pathID;
SAIBool repeat;
uint32 quest;
@@ -1295,7 +1295,7 @@ struct SmartAction
uint32 pathId1;
uint32 pathId2;
uint32 repeat;
uint32 run;
uint32 forcedMovement;
} startClosestWaypoint;
struct