1010#include " WorldHandlers/Transports.h"
1111#include " movement/MoveSplineInit.h"
1212#include " movement/MoveSpline.h"
13+ #include " Creature.h"
1314
1415using namespace ai ;
1516
@@ -33,7 +34,7 @@ bool MovementAction::MoveNear(WorldObject* target, float distance)
3334 {
3435 bool moved = MoveTo (target->GetMapId (),
3536 target->GetPositionX () + cos (angle) * distance,
36- target->GetPositionY ()+ sin (angle) * distance,
37+ target->GetPositionY () + sin (angle) * distance,
3738 target->GetPositionZ ());
3839 if (moved)
3940 {
@@ -43,14 +44,17 @@ bool MovementAction::MoveNear(WorldObject* target, float distance)
4344 return false ;
4445}
4546
46- bool MovementAction::MoveTo (uint32 mapId, float x, float y, float z)
47+ bool MovementAction::MoveTo (uint32 mapId, float x, float y, float z, bool unsafe )
4748{
4849 bot->UpdateGroundPositionZ (x, y, z);
4950 if (!IsMovingAllowed (mapId, x, y, z))
5051 {
5152 return false ;
5253 }
5354
55+ if (!unsafe && ai->HasStrategy (" cautious" ) && IsAggroPosition (x, y))
56+ return false ;
57+
5458 float distance = bot->GetDistance (x, y, z);
5559 if (distance > sPlayerbotAIConfig .contactDistance )
5660 {
@@ -77,7 +81,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z)
7781 mm.Clear ();
7882
7983 float botZ = bot->GetPositionZ ();
80- mm.MovePoint (mapId, x, y, z);
84+ mm.MovePoint (mapId, x, y, z);
8185 }
8286
8387 AI_VALUE (LastMovement&, " last movement" ).Set (x, y, z, bot->GetOrientation ());
@@ -93,7 +97,6 @@ bool MovementAction::MoveTo(Unit* target, float distance)
9397
9498 float bx = bot->GetPositionX ();
9599 float by = bot->GetPositionY ();
96- float bz = bot->GetPositionZ ();
97100
98101 float tx = target->GetPositionX ();
99102 float ty = target->GetPositionY ();
@@ -116,7 +119,20 @@ bool MovementAction::MoveTo(Unit* target, float distance)
116119 float dx = cos (angle) * needToGo + bx;
117120 float dy = sin (angle) * needToGo + by;
118121
119- return MoveTo (target->GetMapId (), dx, dy, tz);
122+ if (needToGo > 0 )
123+ {
124+ float safeDist = CalculateAggroFreeDistance (bx, by, angle, needToGo);
125+ if (safeDist < needToGo)
126+ {
127+ if (safeDist < sPlayerbotAIConfig .contactDistance )
128+ {
129+ return false ;
130+ }
131+ dx = cos (angle) * safeDist + bx;
132+ dy = sin (angle) * safeDist + by;
133+ }
134+ }
135+ return MoveTo (target->GetMapId (), dx, dy, tz, true );
120136}
121137
122138float MovementAction::GetFollowAngle ()
@@ -360,6 +376,11 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
360376 ai->InterruptSpell ();
361377 }
362378
379+ float followX = target->GetPositionX () + cos (angle) * distance;
380+ float followY = target->GetPositionY () + sin (angle) * distance;
381+ if (IsAggroPosition (followX, followY))
382+ return false ;
383+
363384 mm.MoveFollow (target, distance, angle);
364385
365386 AI_VALUE (LastMovement&, " last movement" ).Set (target);
@@ -419,6 +440,74 @@ bool MovementAction::Flee(Unit *target)
419440 return MoveTo (target->GetMapId (), rx, ry, rz);
420441}
421442
443+ /*
444+ * Returns the farthest distance along the beeline from (bx,by) at the
445+ * given angle that doesn't enter any hostile creature's aggro zone.
446+ * Returns maxDist if the entire path is clear.
447+ */
448+ float MovementAction::CalculateAggroFreeDistance (float bx, float by,
449+ float angle, float maxDist)
450+ {
451+ if ( !ai->HasStrategy (" cautious" ))
452+ {
453+ return maxDist;
454+ }
455+ float cosA = cos (angle);
456+ float sinA = sin (angle);
457+ float safeDist = maxDist;
458+
459+ list<ObjectGuid> targets = AI_VALUE (list<ObjectGuid>, " possible targets" );
460+ for (list<ObjectGuid>::iterator i = targets.begin (); i != targets.end (); ++i)
461+ {
462+ Unit* unit = ai->GetUnit (*i);
463+ if (!unit || !unit->IsAlive () || unit->IsInCombat () || !unit->IsHostileTo (bot) || unit == bot->getVictim ())
464+ continue ;
465+
466+ Creature* creature = dynamic_cast <Creature*>(unit);
467+ if (!creature || !creature->CanInitiateAttack ())
468+ continue ;
469+
470+ float aggroRange = creature->GetAttackDistance (bot);
471+ float ex = bx - creature->GetPositionX ();
472+ float ey = by - creature->GetPositionY ();
473+ float b = ex * cosA + ey * sinA;
474+ float c = ex * ex + ey * ey - aggroRange * aggroRange;
475+
476+ float disc = b * b - c;
477+ if (disc < 0 )
478+ continue ;
479+
480+ float sqrtDisc = sqrt (disc);
481+ float tEntry = -b - sqrtDisc;
482+ if (tEntry < 0 )
483+ {
484+ continue ;
485+ }
486+
487+ if (tEntry < safeDist)
488+ {
489+ safeDist = std::max (0 .0f , tEntry - 2 .0f );
490+ }
491+ }
492+
493+ return safeDist;
494+ }
495+
496+ bool MovementAction::IsAggroPosition (float x, float y)
497+ {
498+ float bx = bot->GetPositionX ();
499+ float by = bot->GetPositionY ();
500+
501+ float dx = x - bx;
502+ float dy = y - by;
503+ float dist = sqrt (dx * dx + dy * dy);
504+ if (dist < 0 .1f )
505+ return false ;
506+
507+ float angle = atan2 (dy, dx);
508+ return CalculateAggroFreeDistance (bx, by, angle, dist) < dist;
509+ }
510+
422511bool FleeAction::Execute (Event event)
423512{
424513 return Flee (AI_VALUE (Unit*, " current target" ));
0 commit comments