fix to allow large distances for random movement mobs

Anyone who has tried to set up mobs with random movement types and large distances knows this does not work well in the current core. Since each direction is treated as a single movement, this results in long paths that force the mob walk obliviously through intervening hills or to “fly” over valleys.

I have tested a change to the current codebase that solves this problem by breaking up long pathing movements into smaller moves along a straight line. This means that intermediate points on the path are handled individually, allowing the mob to properly move over hills and through valleys without glitching.

This works really well and I’m pleased that it was so easy to do.

I’ve never released code to this project, nor used git beyond retrieving the source. I am a Java developer by day and we use a different SCM tool. If there is an easy way for me to incorporate this code into the project, I’ll be happy to do so unless you guys restrict that activity to just a few developers (what I assume).

Anyway, here are the changes… I tried to attach the source files, but I didn’t have permission to do so.

In RandomMovementGenerator.h, a new variable called “curr_angle” is added to maintain a mob’s current direction. The private area of this file should look like this:

=====================================

REMOVED… see later post with changed in spoiler tags

==============================

I’ve edited the original post to reflect further changes on testing. The Z-axis is now handled better for fliers – it is calculated as a delta from their height above the ground at their spawn point. Also, the mobs will “run” 10% of the time.

If you only changed that file, i sugest you to use git diff and post the result here.

Also, if the text is to long use

[SPOILER]bbcode.

[/SPOILER]

ok, here are the git diffs for 3 mob movement-related changes that I have been running on my test server.

Fix for Creature Formations where followers get their Z-axis calculated incorrectly.

CreatureGroups.cpp

[SPOILER]diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp

index 8823a78…3c3c348 100755

— a/src/server/game/Entities/Creature/CreatureGroups.cpp

+++ b/src/server/game/Entities/Creature/CreatureGroups.cpp

@@ -232,7 +232,7 @@ void CreatureGroup::LeaderMoveTo(float x, float y, float z)

Trinity::NormalizeMapCoord(dx);

Trinity::NormalizeMapCoord(dy);

  •   member->UpdateGroundPositionZ(dx, dy, dz);
    
  • dz = member->GetBaseMap()->GetHeight(member->GetPhaseMask(), dx, dy, MAX_HEIGHT);

if (member->IsWithinDist(m_leader, dist + MAX_DESYNC))

member->SetUnitMovementFlags(m_leader->GetUnitMovementFlags());

[/SPOILER]

Fix for Random Movement Generator to allow larger distances (2 files)

RandomMovementGenerator.h

[SPOILER]diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h

index 07ec364…eaf8ce3 100755

— a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h

+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.h

@@ -39,6 +39,8 @@ class RandomMovementGenerator : public MovementGeneratorMedium< T, RandomMovemen

uint32 i_nextMove;

float wander_distance;

  • float curr_angle;

  • bool running;

};

#endif

[/SPOILER]

RandomMovementGenerator.cpp

diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp

index 84cd9e8…05938e4 100755

— a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp

+++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp

@@ -43,11 +43,27 @@ void RandomMovementGenerator::_setRandomLocation(Creature& creature)

//bool is_land_ok = creature.CanWalk(); // not used?

//bool is_water_ok = creature.CanSwim(); // not used?

bool is_air_ok = creature.CanFly();

  • const float angle = float(rand_norm()) * static_cast(M_PI*2.0f);

  • const float range = float(rand_norm()) * wander_distance;

  • const float distanceX = range * cos(angle);

  • const float distanceY = range * sin(angle);

  • // curr_angle can be set to -1 if previous move attempt failed and we need to try a new direction

  • float prev_angle = curr_angle;

  • if (curr_angle < 0)

  • curr_angle = float(rand_norm()) * static_cast(M_PI*2.0f);

  • // step distance is greater for flyers since they are less affected by ground changes

  • // and the animation resynch with each step is more noticeable

  • float max_step_distance = (is_air_ok) ? 20.0 : 5.0;

  • float step_distance = (wander_distance > max_step_distance) ? max_step_distance : wander_distance;

  • float distanceX = step_distance * cos(curr_angle) + creature.GetPositionX() - respX;

  • float distanceY = step_distance * sin(curr_angle) + creature.GetPositionY() - respY;

  • float newDistXY = sqrtf(distanceXdistanceX + distanceYdistanceY);

  • while (newDistXY > wander_distance)

  • {

  • curr_angle = float(rand_norm()) * static_cast(M_PI*2.0f);

  • distanceX = step_distance * cos(curr_angle) + creature.GetPositionX() - respX;

  • distanceY = step_distance * sin(curr_angle) + creature.GetPositionY() - respY;

  • newDistXY = sqrtf(distanceXdistanceX + distanceYdistanceY);

  • }

destX = respX + distanceX;

destY = respY + distanceY;

@@ -60,14 +76,11 @@ void RandomMovementGenerator::_setRandomLocation(Creature& creature)

if (is_air_ok) // 3D system above ground and above water (flying mode)

{

  •   // Limit height change
    
  •   const float distanceZ = float(rand_norm()) * sqrtf(travelDistZ)/2.0f;
    
  •   destZ = respZ + distanceZ;
    
  •   float levelZ = map->GetWaterOrGroundLevel(destX, destY, destZ-2.0f);
    
  •   // Problem here, we must fly above the ground and water, not under. Let's try on next tick
    
  •   if (levelZ >= destZ)
    
  •   	return;
    
  •   // Maintain same height as at spawn point (minimum 2 Z)
    
  • float spawnHeight = respZ - map->GetHeight(creature.GetPhaseMask(), respX, respY, MAX_HEIGHT);

  • if (spawnHeight < 2.0)

  • spawnHeight = 2.0;

  • destZ = spawnHeight + map->GetHeight(creature.GetPhaseMask(), destX, destY, MAX_HEIGHT);

}

//else if (is_water_ok) // 3D system under water and above ground (swimming mode)

else // 2D only

@@ -90,22 +103,32 @@ void RandomMovementGenerator::_setRandomLocation(Creature& creature)

destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, true);

// let’s forget this bad coords where a z cannot be find and retry at next tick

  •   		if (fabs(destZ - respZ) > travelDistZ)
    
  • if (fabs(destZ - respZ) > travelDistZ) {

  • curr_angle = -1; // try new direction on next tick

return;

  • }

}

}

}

if (is_air_ok)

i_nextMoveTime.Reset(0);

  • else
  • else if (prev_angle == curr_angle)

  •   i_nextMoveTime.Reset(0);
    
  • else { // if we are changing directions, don’t move on this tick

  • destX = creature.GetPositionX();

  • destY = creature.GetPositionY();

  • destZ = creature.GetPositionZ();

  • running = urand(1,100) <= RUNNING_CHANCE_RANDOMMV;

i_nextMoveTime.Reset(urand(500, 10000));

  • }

creature.AddUnitState(UNIT_STATE_ROAMING_MOVE);

Movement::MoveSplineInit init(creature);

init.MoveTo(destX, destY, destZ);

  • init.SetWalk(true);
  • init.SetWalk(!running);

init.Launch();

//Call for creature group update

This should use UpdateAllowedPositionZ

I’m at work now. Can you elaborate?

UpdateAllowedPositionZ updates the Z coordinate according to the creature capabilities, if it can fly, and the Z axis is above ground level, it remains untouched, otherwise its set to the ground level including dynamic line of sight and the static line of sight.

So it pretty much does the same as GetHeight but its a bit more smart.

Outstanding! I’ll make those changes tonight and see how they work out. Of course, my creature formations are not flying so I’ll not sure if I’ll be able to confirm that behavior. I am a stickler for immersion and mobs glitching over terrain or simply bouncing around in a tiny radius really drives me nuts.

I did make the suggested changes earlier and started getting some assert errors when the world server started. Going back to the changes I posted cleaned them up.

I’m not exactly sure what is causing those errors, but I’m not particularly inclined to debug them since what I have seems to be working so well already. It’s pretty cool being able to set up random pathing creature formations (like kodo or wolf packs) and have them move through large swaths of the zone without ever glitching out on the terrain.

The birds also fly well, adjusting their Z-axis to remain a constant height above the terrain.

baric:

How weird, the asserts.

Anyways, could you make a PullRequest so it can be seen by more people and reviewed?

Would love to do so. I’ve never made a PullRequest here before… how do you do that? [ NVM: found an faq on it… will do it when I get home tonight]

Also, I wrote a quick Java program today to generate creature_formations from a list of guids. It works awesome! All of my 300+ wolves in Mulgore are now roaming the plains in 3-packs, led by an Alpha /emoticons/default_tongue.png

(not Blizz-like, but very cool nonetheless)