diff --git a/doc/RequiredPorts.mangosproof b/doc/RequiredPorts.mangosproof index 52b26886..9175cb1a 100644 --- a/doc/RequiredPorts.mangosproof +++ b/doc/RequiredPorts.mangosproof @@ -1 +1 @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/src/game/MotionGenerators/ConfusedMovementGenerator.cpp b/src/game/MotionGenerators/ConfusedMovementGenerator.cpp index 0367719d..8a5abb6f 100644 --- a/src/game/MotionGenerators/ConfusedMovementGenerator.cpp +++ b/src/game/MotionGenerators/ConfusedMovementGenerator.cpp @@ -89,25 +89,21 @@ bool ConfusedMovementGenerator::Update(T& unit, const uint32& diff) // start moving unit.addUnitState(UNIT_STAT_CONFUSED_MOVE); - float x = i_x + 10.0f * (rand_norm_f() - 0.5f); - float y = i_y + 10.0f * (rand_norm_f() - 0.5f); - float z = i_z; + float destX = i_x; + float destY = i_y; + float destZ = i_z; - unit.UpdateAllowedPositionZ(x, y, z); - - PathFinder path(&unit); - path.setPathLengthLimit(30.0f); - path.calculate(x, y, z); - if (path.getPathType() & PATHFIND_NOPATH) + // check if new random position is assigned, GetReachableRandomPosition may fail + if (unit.GetMap()->GetReachableRandomPosition(&unit, destX, destY, destZ, 10.0f)) { - i_nextMoveTime.Reset(urand(800, 1000)); - return true; + Movement::MoveSplineInit init(unit); + init.MoveTo(destX, destY, destZ, true); + init.SetWalk(true); + init.Launch(); + i_nextMoveTime.Reset(urand(800, 1000)); // Keep a short wait time } - - Movement::MoveSplineInit init(unit); - init.MovebyPath(path.getPath()); - init.SetWalk(true); - init.Launch(); + else + i_nextMoveTime.Reset(50); // Retry later } } @@ -118,7 +114,7 @@ template<> void ConfusedMovementGenerator::Finalize(Player& unit) { unit.clearUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_CONFUSED_MOVE); - unit.StopMoving(); + unit.StopMoving(true); } template<> diff --git a/src/game/MotionGenerators/FleeingMovementGenerator.cpp b/src/game/MotionGenerators/FleeingMovementGenerator.cpp index 53ae2b06..38453bd5 100644 --- a/src/game/MotionGenerators/FleeingMovementGenerator.cpp +++ b/src/game/MotionGenerators/FleeingMovementGenerator.cpp @@ -46,7 +46,11 @@ void FleeingMovementGenerator::_setTargetLocation(T& owner) float x, y, z; if (!_getPoint(owner, x, y, z)) - { return; } + { + // random point not found recheck later + i_nextCheckTime.Reset(50); + return; + } owner.addUnitState(UNIT_STAT_FLEEING_MOVE); @@ -55,7 +59,8 @@ void FleeingMovementGenerator::_setTargetLocation(T& owner) path.calculate(x, y, z); if (path.getPathType() & PATHFIND_NOPATH) { - i_nextCheckTime.Reset(urand(1000, 1500)); + // path not found recheck later + i_nextCheckTime.Reset(50); return; } @@ -109,12 +114,23 @@ bool FleeingMovementGenerator::_getPoint(T& owner, float& x, float& y, float& x = curr_x + dist * cos(angle); y = curr_y + dist * sin(angle); - z = curr_z; + z = curr_z + 0.5f; + + // try to fix z + if (!owner.GetMap()->GetHeightInRange(x, y, z)) + return false; if (owner.GetTypeId() == TYPEID_PLAYER) - { owner.GetMap()->GetHitPosition(curr_x, curr_y, curr_z, x, y, z, -0.1f); } - - owner.UpdateAllowedPositionZ(x, y, z); + { + // check any collision + float testZ = z + 0.5f; // needed to avoid some false positive hit detection of terrain or passable little object + if (owner.GetMap()->GetHitPosition(curr_x, curr_y, curr_z + 0.5f, x, y, testZ, -0.1f)) + { + z = testZ; + if (!owner.GetMap()->GetHeightInRange(x, y, z)) + return false; + } + } return true; } diff --git a/src/game/MotionGenerators/RandomMovementGenerator.cpp b/src/game/MotionGenerators/RandomMovementGenerator.cpp index 119f9fc2..6a0302a5 100644 --- a/src/game/MotionGenerators/RandomMovementGenerator.cpp +++ b/src/game/MotionGenerators/RandomMovementGenerator.cpp @@ -40,39 +40,32 @@ RandomMovementGenerator::RandomMovementGenerator(const Creature& creat i_y = respY; i_z = respZ; i_radius = wander_distance; - // TODO - add support for flying mobs using some distance - i_verticalZ = 0.0f; } template<> void RandomMovementGenerator::_setRandomLocation(Creature& creature) { - const float angle = rand_norm_f() * (M_PI_F * 2.0f); - const float range = rand_norm_f() * i_radius; - - const float maxPathRange = range * 1.5f; - - float destX = i_x + range * cos(angle); - float destY = i_y + range * sin(angle); - float destZ = i_z + frand(-1, 1) * i_verticalZ; - creature.UpdateAllowedPositionZ(destX, destY, destZ); + float destX = i_x; + float destY = i_y; + float destZ = i_z; creature.addUnitState(UNIT_STAT_ROAMING_MOVE); - Movement::MoveSplineInit init(creature); - init.MoveTo(destX, destY, destZ, true, false, maxPathRange); - init.SetWalk(true); - init.Launch(); - - if (creature.CanFly()) - { i_nextMoveTime.Reset(0); } - else + // check if new random position is assigned, GetReachableRandomPosition may fail + if (creature.GetMap()->GetReachableRandomPosition(&creature, destX, destY, destZ, i_radius)) { + Movement::MoveSplineInit init(creature); + init.MoveTo(destX, destY, destZ, true); + init.SetWalk(true); + init.Launch(); if (roll_chance_i(MOVEMENT_RANDOM_MMGEN_CHANCE_NO_BREAK)) i_nextMoveTime.Reset(50); else - i_nextMoveTime.Reset(urand(3000, 10000)); // keep a short wait time + i_nextMoveTime.Reset(urand(3000, 10000)); // Keep a short wait time } + else + i_nextMoveTime.Reset(50); // Retry later + return; } template<> diff --git a/src/game/WorldHandlers/Map.cpp b/src/game/WorldHandlers/Map.cpp index d3621b2f..1f9d6760 100644 --- a/src/game/WorldHandlers/Map.cpp +++ b/src/game/WorldHandlers/Map.cpp @@ -2050,6 +2050,61 @@ bool Map::GetHitPosition(float srcX, float srcY, float srcZ, float& destX, float return result0 || result1; } +// Find an height within a reasonable range of provided Z. This method may fail so we have to handle that case. +bool Map::GetHeightInRange(float x, float y, float& z, float maxSearchDist /*= 4.0f*/) const +{ + float height, vmapHeight, mapHeight; + vmapHeight = VMAP_INVALID_HEIGHT_VALUE; + + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + if (!vmgr->isLineOfSightCalcEnabled()) + vmgr = NULL; + + if (vmgr) + { + // pure vmap search + vmapHeight = vmgr->getHeight(i_id, x, y, z + 2.0f, maxSearchDist + 2.0f); + } + + // find raw height from .map file on X,Y coordinates + if (GridMap* gmap = const_cast(m_TerrainData)->GetGrid(x, y)) // TODO:: find a way to remove that const_cast + mapHeight = gmap->getHeight(x, y); + + float diffMaps = fabs(fabs(z) - fabs(mapHeight)); + float diffVmaps = fabs(fabs(z) - fabs(vmapHeight)); + if (diffVmaps < maxSearchDist) + { + if (diffMaps < maxSearchDist) + { + // well we simply have to take the highest as normally there we cannot be on top of cavern is maxSearchDist is not too big + if (vmapHeight > mapHeight) + height = vmapHeight; + else + height = mapHeight; + + //sLog.outString("vmap %5.4f, map %5.4f, height %5.4f", vmapHeight, mapHeight, height); + } + else + { + //sLog.outString("vmap %5.4f", vmapHeight); + height = vmapHeight; + } + } + else + { + if (diffMaps < maxSearchDist) + { + //sLog.outString("map %5.4f", mapHeight); + height = mapHeight; + } + else + return false; + } + + z = std::max(height, m_dyn_tree.getHeight(x, y, height + 1.0f, maxSearchDist)); + return true; +} + float Map::GetHeight(float x, float y, float z) const { float staticHeight = m_TerrainData->GetHeightStatic(x, y, z); @@ -2073,3 +2128,178 @@ bool Map::ContainsGameObjectModel(const GameObjectModel& mdl) const { return m_dyn_tree.contains(mdl); } + +// This will generate a random point to all directions in water for the provided point in radius range. +bool Map::GetRandomPointUnderWater(float& x, float& y, float& z, float radius, GridMapLiquidData& liquid_status) +{ + const float angle = rand_norm_f() * (M_PI_F * 2.0f); + const float range = rand_norm_f() * radius; + + float i_x = x + range * cos(angle); + float i_y = y + range * sin(angle); + + // get real ground of new point + // the code consider cylinder instead of sphere for possible z + float ground = GetHeight(i_x, i_y, z); + if (ground > INVALID_HEIGHT) // GetHeight can fail + { + float min_z = z - 0.7f * radius; // 0.7 to have a bit a "flat" cylinder, TODO which value looks nicest + if (min_z < ground) + min_z = ground + 0.5f; // Get some space to prevent under map + + float liquidLevel = liquid_status.level - 2.0f; // just to make the generated point is in water and not on surface or a bit above + + // if not enough space to fit the creature better is to return from here + if (min_z > liquidLevel) + return false; + + float max_z = std::max(z + 0.7f * radius, min_z); + max_z = std::min(max_z, liquidLevel); + x = i_x; + y = i_y; + z = min_z + rand_norm_f() * (max_z - min_z); + return true; + } + return false; +} + +// This will generate a random point to all directions in air for the provided point in radius range. +bool Map::GetRandomPointInTheAir(float& x, float& y, float& z, float radius) +{ + const float angle = rand_norm_f() * (M_PI_F * 2.0f); + const float range = rand_norm_f() * radius; + + float i_x = x + range * cos(angle); + float i_y = y + range * sin(angle); + + // get real ground of new point + // the code consider cylinder instead of sphere for possible z + float ground = GetHeight(i_x, i_y, z); + if (ground > INVALID_HEIGHT) // GetHeight can fail + { + float min_z = z - 0.7f * radius; // 0.7 to have a bit a "flat" cylinder, TODO which value looks nicest + if (min_z < ground) + min_z = ground + 2.5f; // Get some space to prevent landing + float max_z = std::max(z + 0.7f * radius, min_z); + x = i_x; + y = i_y; + z = min_z + rand_norm_f() * (max_z - min_z); + return true; + } + return false; +} + +// supposed to be used for not big radius, usually less than 20.0f +bool Map::GetReachableRandomPointOnGround(float& x, float& y, float& z, float radius) +{ + // Generate a random range and direction for the new point + const float angle = rand_norm_f() * (M_PI_F * 2.0f); + const float range = rand_norm_f() * radius; + + float i_x = x + range * cos(angle); + float i_y = y + range * sin(angle); + float i_z = z + 1.0f; + + GetHitPosition(x, y, z + 1.0f, i_x, i_y, i_z, -0.5f); + i_z = z; // reset i_z to z value to avoid too much difference from original point before GetHeightInRange + if (!GetHeightInRange(i_x, i_y, i_z)) // GetHeight can fail + return false; + + // here we have a valid position but the point can have a big Z in some case + // next code will check angle from 2 points + // c + // /| + // / | + // b/__|a + + // project vector to get only positive value + float ab = fabs(x - i_x); + float ac = fabs(z - i_z); + + // slope represented by c angle (in radian) + float slope = 0; + const float MAX_SLOPE_IN_RADIAN = 50.0f / 180.0f * M_PI_F; // 50(degree) max seem best value for walkable slope + + // check ab vector to avoid divide by 0 + if (ab > 0.0f) + { + // compute c angle and convert it from radian to degree + slope = atan(ac / ab); + if (slope < MAX_SLOPE_IN_RADIAN) + { + x = i_x; + y = i_y; + z = i_z; + return true; + } + } + + return false; +} + +// Get random point by handling different situation depending of if the unit is flying/swimming/walking +bool Map::GetReachableRandomPosition(Unit* unit, float& x, float& y, float& z, float radius) +{ + + float i_x = x; + float i_y = y; + float i_z = z; + + bool newDestAssigned = false; // used to check if new random destination is found + + bool isFlying = false; + bool isSwimming = true; + switch (unit->GetTypeId()) + { + case TYPEID_PLAYER: + isFlying = static_cast(unit)->IsFlying(); + break; + case TYPEID_UNIT: + isFlying = static_cast(unit)->IsFlying(); + isSwimming = static_cast(unit)->IsSwimming(); + break; + default: + sLog.outError("Map::GetReachableRandomPosition> Unsupported unit type is passed!"); + return false; + } + + if (radius < 0.1f) + { + sLog.outError("Map::GetReachableRandomPosition> Unsupported unit type is passed!"); + return false; + } + + if (isFlying) + { + newDestAssigned = GetRandomPointInTheAir(i_x, i_y, i_z, radius); + /*if (newDestAssigned) + sLog.outString("Generating air random point for %s", GetGuidStr().c_str());*/ + } + else + { + GridMapLiquidData liquid_status; + GridMapLiquidStatus res = m_TerrainData->getLiquidStatus(i_x, i_y, i_z, MAP_ALL_LIQUIDS, &liquid_status); + if (isSwimming && (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER))) + { + newDestAssigned = GetRandomPointUnderWater(i_x, i_y, i_z, radius, liquid_status); + /*if (newDestAssigned) + sLog.outString("Generating swim random point for %s", GetGuidStr().c_str());*/ + } + else + { + newDestAssigned = GetReachableRandomPointOnGround(i_x, i_y, i_z, radius); + /*if (newDestAssigned) + sLog.outString("Generating ground random point for %s", GetGuidStr().c_str());*/ + } + } + + if (newDestAssigned) + { + x = i_x; + y = i_y; + z = i_z; + return true; + } + + return false; +} diff --git a/src/game/WorldHandlers/Map.h b/src/game/WorldHandlers/Map.h index 36ad2d22..8140fb4f 100644 --- a/src/game/WorldHandlers/Map.h +++ b/src/game/WorldHandlers/Map.h @@ -292,6 +292,11 @@ class Map : public GridRefManager */ void SetWeather(uint32 zoneId, WeatherType type, float grade, bool permanently); + // Random on map generation + bool GetReachableRandomPosition(Unit* unit, float& x, float& y, float& z, float radius); + bool GetReachableRandomPointOnGround(float& x, float& y, float& z, float radius); + bool GetRandomPointInTheAir(float& x, float& y, float& z, float radius); + bool GetRandomPointUnderWater(float& x, float& y, float& z, float radius, GridMapLiquidData& liquid_status); private: void LoadMapAndVMap(int gx, int gy);