Index: src/game/RandomMovementGenerator.cpp =================================================================== --- src/game/RandomMovementGenerator.cpp (revision 5788) +++ src/game/RandomMovementGenerator.cpp (working copy) @@ -21,67 +21,134 @@ #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::Initialize(Creature &creature) { - float x,y,z,z2, wander_distance; + float x,y,z,wander_distance; + float waypoints[(ANGLE_PRECISSION*RANGE_PRECISSION)+1][3]; + int waypoint_count = 1; + creature.GetRespawnCoord(x, y, z,NULL,&wander_distance); uint32 mapid=creature.GetMapId(); + //float go_height[ANGLE_PRECISSION][RANGE_PRECISSION]; + Map const* map = MapManager::Instance().GetBaseMap(mapid); - // Initialization is done in bulk. Don’t 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(); + // We need vmap manager, to make creatures walk properly over the objects + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + 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. Don’t 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; + 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; + } + } + } } i_nextMoveTime.Reset(urand(0, 10000-1)); // TODO: check the lower bound (it is probably too small) creature.StopMoving(); @@ -105,10 +172,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 +185,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