Index: src/game/RandomMovementGenerator.cpp===================================================================--- src/game/RandomMovementGenerator.cpp (revision 5789)+++ src/game/RandomMovementGenerator.cpp (working copy)@@ -21,69 +21,220 @@ #include "Opcodes.h" #include "RandomMovementGenerator.h" #include "DestinationHolderImp.h"+#include "VMapFactory.h"+#include "Map.h"-/*-Here interpolation is disabled by default due to it causing crashing-in some compile environments.-If your server can handle the small amount of lag this may cause-and you can build without experiencing a significant increase in crashes-then you may uncomment the following line to have correct random motion.-*/-// uncomment now for wide testing purpose-#define USE_INTERPOLATION+/**+ * Those define the size of the table of the ground/object heights around the spawn point,+ * bigger they are, more time is needed to calculate.+ */+#define ANGLE_PRECISSION 8+#define RANGE_PRECISSION 8 template<> void RandomMovementGenerator<Creature>::Initialize(Creature &creature) {- float x,y,z,z2, wander_distance;- creature.GetRespawnCoord(x, y, z,NULL,&wander_distance);- uint32 mapid=creature.GetMapId();+ QueryResult *result = NULL;+ bool cached = false;+ // Try cache+ result = WorldDatabase.PQuery("SELECT position_x, position_y, position_z FROM cache_random_movement_generator WHERE guid = '%u' ORDER BY point", creature.GetGUIDLow()); + if( result ) {+ if(result->GetRowCount()==MAX_RAND_WAYPOINTS + 1)+ {+ for(unsigned int idx = 0; idx < MAX_RAND_WAYPOINTS + 1; ++idx)+ {+ Field *fields = result->Fetch();+ // Read from cache+ i_waypoints[idx][0] = fields[0].GetFloat();+ i_waypoints[idx][1] = fields[1].GetFloat();+ i_waypoints[idx][2] = fields[2].GetFloat();+ result->NextRow();+ }+ // Mark creature as loaded from cache+ cached = true;+ }+ }+ // If we couldn't load creature from cache, do all the calc+ if(!cached)+ {+ // We need vmap manager, to make creatures walk properly over the objects+ VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager();+ // To use new algorithm, vmaps are needed, if not, just fall back to the previous one+ bool vmaps_enabled = vmgr->isLineOfSightCalcEnabled() && vmgr->isHeightCalcEnabled();+ if(vmaps_enabled)+ {+ float x,y,z,wander_distance;+ float waypoints[(ANGLE_PRECISSION*RANGE_PRECISSION)+1][3];+ int waypoint_count = 1;- Map const* map = MapManager::Instance().GetBaseMap(mapid);- // Initialization is done in bulk. Dont use vamps for that (4. parameter = false). It is too costly when entering a new map grid- z2 = map->GetHeight(x,y,z, false); // use .map base surface height- if( fabs( z2 - z ) < 5 )- z = z2;+ creature.GetRespawnCoord(x, y, z,NULL,&wander_distance);+ uint32 mapid=creature.GetMapId();- i_nextMove = 1;- i_waypoints[0][0] = x;- i_waypoints[0][1] = y;- i_waypoints[0][2] = z;+ Map const* map = MapManager::Instance().GetBaseMap(mapid);- bool is_water_ok = creature.isCanSwimOrFly();- bool is_land_ok = creature.isCanWalkOrFly();+ bool is_water_ok = creature.isCanSwimOrFly();+ bool is_land_ok = creature.isCanWalkOrFly();- for(unsigned int idx=1; idx < MAX_RAND_WAYPOINTS+1; ++idx)- {- const float angle = 2*M_PI*rand_norm();- const float range = wander_distance*rand_norm();+ float step = wander_distance/RANGE_PRECISSION;- i_waypoints[idx][0] = x+ range * cos(angle);- i_waypoints[idx][1] = y+ range * sin(angle);+ waypoints[0][0] = x;+ waypoints[0][1] = y;+ waypoints[0][2] = z;+ // Build the table of ground/object heights around spawn point+ for(int a = 0; a < ANGLE_PRECISSION; a++)+ {+ float angle = (2*M_PI/ANGLE_PRECISSION) * (float)a;+ // We use the previous Z value as a reference point to find out the map height+ float last_z = z;+ for(int r = 0; r < RANGE_PRECISSION; r++)+ {+ float range = step * ((float) r + 1.0);+ // Calculate the test around the spawn point+ float px = x + range * cos(angle);+ float py = y + range * sin(angle);+ MaNGOS::NormalizeMapCoord(px);+ MaNGOS::NormalizeMapCoord(py);+ float map_height = map->GetHeight(px, py , last_z, true);+ float obj_height;- // prevent invalid coordinates generation- MaNGOS::NormalizeMapCoord(i_waypoints[idx][0]);- MaNGOS::NormalizeMapCoord(i_waypoints[idx][1]);+ // Calculate the object height + vmgr->getObjectHitPos(mapid, + px, py, last_z + 2.0f,+ px, py, ((map_height!=VMAP_INVALID_HEIGHT_VALUE) ? (map_height) : (last_z-2.0f)),+ px, py, obj_height,+ 0.0f);- bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z);- // if generated wrong path just ignore- if( is_water && !is_water_ok || !is_water && !is_land_ok )- {- i_waypoints[idx][0] = i_waypoints[idx-1][0];- i_waypoints[idx][1] = i_waypoints[idx-1][1];- i_waypoints[idx][2] = i_waypoints[idx-1][2];- continue;- }+ float pz = VMAP_INVALID_HEIGHT_VALUE;+ if(obj_height>last_z-2.0f)+ {+ // Z value in the map will be the bigger of the two: map or object+ pz = (((map_height) > (obj_height)) ? (map_height) : (obj_height)); + }- // Initialization is done in bulk. Dont use vamps for that (4. parameter = false).- // It is too costly when entering a new map grid, use .map base surface height- z2 = map->GetHeight(i_waypoints[idx][0],i_waypoints[idx][1],z, false);- if( fabs( z2 - z ) < 5 )- z = z2;- i_waypoints[idx][2] = z;- }- i_nextMoveTime.Reset(urand(0, 10000-1)); // TODO: check the lower bound (it is probably too small)+ if(pz!=VMAP_INVALID_HEIGHT_VALUE)+ {+ bool is_water = map->IsInWater(px,py,pz); + // If it is bad place for the creature (water for non water creature, ect), just mark it as invalid.+ if(is_water && !is_water_ok || !is_water && !is_land_ok)+ { + pz = VMAP_INVALID_HEIGHT_VALUE;+ }++ // If point is not invalid, add to possible walk points+ if(pz!=VMAP_INVALID_HEIGHT_VALUE)+ {+ last_z = pz;+ waypoints[waypoint_count][0] = px;+ waypoints[waypoint_count][1] = py;+ waypoints[waypoint_count][2] = pz;+ waypoint_count++;+ }+ }+ }+ } ++ // Here, we calculate the walk points+ i_nextMove = 1;+ // First waypoint is always a spawn point, easy here.+ i_waypoints[0][0] = x;+ i_waypoints[0][1] = y;+ i_waypoints[0][2] = z; ++ for(unsigned int idx=1; idx < MAX_RAND_WAYPOINTS+1; ++idx)+ {+ bool goodPoint = false;+ int r = urand(0, waypoint_count-1);+ while(!goodPoint)+ {+ float dx = waypoints[r][0];+ float dy = waypoints[r][1];+ float dz = waypoints[r][2];+ // And check LOS+ vmgr->getObjectHitPos(mapid, + i_waypoints[0][0], i_waypoints[0][1], i_waypoints[0][2] + creature.GetObjectSize()/2.0f,+ dx, dy, dz + creature.GetObjectSize()/2.0f,+ i_waypoints[idx][0], i_waypoints[idx][1], i_waypoints[idx][2],+ creature.GetObjectSize()*2.0);+ i_waypoints[idx][2] = i_waypoints[idx][2]- creature.GetObjectSize()/2.0f;+ float x_dx = i_waypoints[idx][0] - i_waypoints[idx-1][0];+ float x_dy = i_waypoints[idx][1] - i_waypoints[idx-1][1];+ float x_dist = sqrt((x_dx*x_dx) + (x_dy*x_dy));+ float x_dz = i_waypoints[idx][2]-i_waypoints[idx-1][2];+ // Check if creature is not attempting to do >45 deg. walk+ if(abs(x_dz)<=x_dist)+ {+ goodPoint = true;+ } else {+ // If yes, try next point+ r = r + 1;+ if(r==waypoint_count)+ {+ r = 0;+ }+ }+ }+ }+ // Perform database dump of calculated waypoints to populate cache for future use+ for(unsigned int idx=0; idx < MAX_RAND_WAYPOINTS+1; ++idx)+ {+ WorldDatabase.PExecuteLog("INSERT INTO cache_random_movement_generator (guid, point, position_x, position_y, position_z) VALUES ('%u','%u','%f','%f','%f')", + creature.GetGUIDLow(), idx,+ i_waypoints[idx][0], i_waypoints[idx][1], i_waypoints[idx][2]); + }+ } else {+ // Fallback algorithm (previous one)+ float x,y,z,z2, wander_distance;+ creature.GetRespawnCoord(x, y, z,NULL,&wander_distance);+ uint32 mapid=creature.GetMapId();++ Map const* map = MapManager::Instance().GetBaseMap(mapid);+ // Initialization is done in bulk. Dont use vamps for that (4. parameter = false). It is too costly when entering a new map grid+ z2 = map->GetHeight(x,y,z, false); // use .map base surface height+ if( fabs( z2 - z ) < 5 )+ z = z2;++ i_nextMove = 1;+ i_waypoints[0][0] = x;+ i_waypoints[0][1] = y;+ i_waypoints[0][2] = z;++ bool is_water_ok = creature.isCanSwimOrFly();+ bool is_land_ok = creature.isCanWalkOrFly();++ for(unsigned int idx=1; idx < MAX_RAND_WAYPOINTS+1; ++idx)+ {+ const float angle = 2*M_PI*rand_norm();+ const float range = wander_distance*rand_norm();++ i_waypoints[idx][0] = x+ range * cos(angle);+ i_waypoints[idx][1] = y+ range * sin(angle);++ // prevent invalid coordinates generation+ MaNGOS::NormalizeMapCoord(i_waypoints[idx][0]);+ MaNGOS::NormalizeMapCoord(i_waypoints[idx][1]);++ bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z);+ // if generated wrong path just ignore+ if( is_water && !is_water_ok || !is_water && !is_land_ok )+ {+ i_waypoints[idx][0] = i_waypoints[idx-1][0];+ i_waypoints[idx][1] = i_waypoints[idx-1][1];+ i_waypoints[idx][2] = i_waypoints[idx-1][2];+ continue;+ }++ // Initialization is done in bulk. Dont use vamps for that (4. parameter = false).+ // It is too costly when entering a new map grid, use .map base surface height+ z2 = map->GetHeight(i_waypoints[idx][0],i_waypoints[idx][1],z, false);+ if( fabs( z2 - z ) < 5 )+ z = z2;+ i_waypoints[idx][2] = z;+ }+ }+ }++ i_nextMoveTime.Reset(urand(0, 10000-1)); // TODO: check the lower bound (it is probably too small) creature.StopMoving(); }@@ -105,10 +256,8 @@ if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) return true; i_nextMoveTime.Update(diff);- #ifdef USE_INTERPOLATION CreatureTraveller traveller(creature); i_destinationHolder.UpdateTraveller(traveller, diff, false);- #endif if( i_nextMoveTime.Passed() ) { if( creature.IsStopped() )@@ -120,9 +269,7 @@ creature.addUnitState(UNIT_STAT_ROAMING); CreatureTraveller traveller(creature); i_destinationHolder.SetDestination(traveller, x, y, z);- #ifndef USE_INTERPOLATION traveller.Relocation(x,y,z);- #endif i_nextMoveTime.Reset( i_destinationHolder.GetTotalTravelTime() ); } else