void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float respX, respY, respZ, respO, wander_distance; creature.GetRespawnCoord(respX, respY, respZ, &respO, &wander_distance); const float angle = rand_norm_f() * (M_PI_F*2.0f); const float range = rand_norm_f() * wander_distance; float destX = respX + range * cos(angle); float destY = respY + range * sin(angle); float destZ = creature.GetPositionZ(); creature.UpdateAllowedPositionZ(destX, destY, destZ); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); Movement::MoveSplineInit init(creature); init.MoveTo(destX, destY, destZ, true); init.SetWalk(true); init.Launch(); if (creature.CanFly()) i_nextMoveTime.Reset(0); else i_nextMoveTime.Reset(urand(500, 10000)); }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { const float angle = rand_norm_f() * (M_PI_F*2.0f); const float range = rand_norm_f() * i_radius; float destX,destY,destZ; creature.GetNearPoint(&creature, destX, destY, destZ, creature.GetObjectBoundingRadius(), range, angle); creature.UpdateAllowedPositionZ(destX, destY, destZ); float dx = i_x - destX; float dy = i_y - destY; // TODO: Limitation creatutre travel range. if (sqrt((dx*dx) + (dy*dy)) > i_radius) { destX = i_x; destY = i_y; destZ = i_z; } creature.addUnitState(UNIT_STAT_ROAMING_MOVE); Movement::MoveSplineInit init(creature); init.MoveTo(destX, destY, destZ, true); init.SetWalk(true); init.Launch(); if (creature.CanFly()) i_nextMoveTime.Reset(0); else i_nextMoveTime.Reset(urand(500, 10000)); }
void RandomMovementGenerator<Creature>::_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); 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 { 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 } }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { Position dest; creature.GetRespawnCoord(dest.x, dest.y, dest.z, &dest.o, &wander_distance); bool is_air_ok = creature.CanFly(); const float angle = frand(0.0f, M_PI*2.0f); const float range = frand(0.0f, wander_distance); creature.GetValidPointInAngle(dest, range, angle, false); static_cast<MovementGenerator*>(this)->_recalculateTravel = false; if (is_air_ok) i_nextMoveTime.Reset(0); else { if (roll_chance_i(MOVEMENT_RANDOM_MMGEN_CHANCE_NO_BREAK)) i_nextMoveTime.Reset(0); else i_nextMoveTime.Reset(urand(500, 10000)); } creature.addUnitState(UNIT_STAT_ROAMING); Movement::MoveSplineInit init(creature); init.MoveTo(dest.x, dest.y, dest.z); init.SetWalk(true); init.Launch(); // Call for creature group update if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature) creature.GetFormation()->LeaderMoveTo(dest.x, dest.y, dest.z); }
void WaypointMovementGenerator<Creature>::StartMove(Creature &creature) { if (!i_path || i_path->empty()) return; if (Stopped()) return; if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { if (behavior->model2 != 0) creature.SetDisplayId(behavior->model2); creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } if (m_isArrivalDone) i_currentNode = (i_currentNode+1) % i_path->size(); m_isArrivalDone = false; if (creature.CanFly()) creature.AddSplineFlag(SPLINEFLAG_FLYING); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); const WaypointNode &node = i_path->at(i_currentNode); CreatureTraveller traveller(creature); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); }
bool RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if (creature.hasUnitState(UNIT_STAT_NOT_MOVE)) { i_nextMoveTime.Update(i_nextMoveTime.GetExpiry()); // Expire the timer creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } i_nextMoveTime.Update(diff); if (i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.CanFly()) creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); if (!i_destinationHolder.HasArrived() && creature.IsStopped()) creature.addUnitState(UNIT_STAT_ROAMING_MOVE); CreatureTraveller traveller(creature); if (i_destinationHolder.UpdateTraveller(traveller, diff, false, true)) { if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost if (i_nextMoveTime.Passed()) { float x,y,z; if(i_destinationHolder.HasDestination()) i_destinationHolder.GetLocationNowNoMicroMovement(x,y,z); else creature.GetPosition(x,y,z); if (creature.CanFly() && !(creature.CanWalk() && creature.IsAtGroundLevel(x,y,z))) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); else creature.AddSplineFlag(SPLINEFLAG_WALKMODE); _setRandomLocation(creature); } else if (creature.IsPet() && creature.GetOwner() && !creature.IsWithinDist(creature.GetOwner(), PET_FOLLOW_DIST+2.5f)) { creature.AddSplineFlag(SPLINEFLAG_WALKMODE); _setRandomLocation(creature); } } return true; }
void RandomMovementGenerator<Creature>::Initialize(Creature &creature) { if (!creature.isAlive()) return; if (creature.CanFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); else creature.AddSplineFlag(SPLINEFLAG_WALKMODE); creature.addUnitState(UNIT_STAT_ROAMING|UNIT_STAT_ROAMING_MOVE); _setRandomLocation(creature); }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { Position dest; creature.GetRespawnCoord(dest.x, dest.y, dest.z, &dest.o, &wander_distance); bool is_air_ok = creature.CanFly(); const float angle = frand(0.0f, M_PI*2.0f); const float range = frand(0.0f, wander_distance); creature.GetValidPointInAngle(dest, range, angle, false); Movement::MoveSplineInit init(creature); init.MoveTo(dest.x, dest.y, dest.z); init.SetWalk(true); init.Launch(); static_cast<MovementGenerator*>(this)->_recalculateTravel = false; i_nextMoveTime.Reset(urand(500, 10000)); }
//-----------------------------------------------// void WaypointMovementGenerator<Creature>::LoadPath(Creature &creature) { DETAIL_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "LoadPath: loading waypoint path for creature %u, %u", creature.GetGUIDLow(), creature.GetDBTableGUIDLow()); i_path = sWaypointMgr.GetPath(creature.GetDBTableGUIDLow()); // We may LoadPath() for several occasions: // 1: When creature.MovementType=2 // 1a) Path is selected by creature.guid == creature_movement.id // 1b) Path for 1a) does not exist and then use path from creature.GetEntry() == creature_movement_template.entry // 2: When creature_template.MovementType=2 // 2a) Creature is summoned and has creature_template.MovementType=2 // Creators need to be sure that creature_movement_template is always valid for summons. // Mob that can be summoned anywhere should not have creature_movement_template for example. // No movement found for guid if (!i_path) { i_path = sWaypointMgr.GetPathTemplate(creature.GetEntry()); // No movement found for entry if (!i_path) { sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path", creature.GetName(), creature.GetEntry(), creature.GetDBTableGUIDLow()); return; } } // We have to set the destination here (for the first point), right after Initialize. Without, we may not have valid xyz for GetResetPosition CreatureTraveller traveller(creature); if (creature.CanFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature) { const float angle = rand_norm_f() * (M_PI_F * 2.0f); const float range = rand_norm_f() * i_radius; 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); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); Movement::MoveSplineInit init(creature); init.MoveTo(destX, destY, destZ, true); init.SetWalk(true); init.Launch(); if (creature.CanFly()) i_nextMoveTime.Reset(0); else i_nextMoveTime.Reset(urand(500, 10000)); }
void PathInfo::BuildPolyPath(PathNode startPos, PathNode endPos) { // *** getting start/end poly logic *** float distToStartPoly, distToEndPoly; float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x}; float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x}; dtPolyRef startPoly = getPolyByLocation(startPoint, &distToStartPoly); dtPolyRef endPoly = getPolyByLocation(endPoint, &distToEndPoly); // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying exception ) // its up to caller how he will use this info if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n"); BuildShortcut(); m_type = (m_sourceUnit->GetTypeId() == TYPEID_UNIT && ((Creature*)m_sourceUnit)->CanFly()) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; return; } // we may need a better number here bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly); bool buildShotrcut = false; if (m_sourceUnit->GetTypeId() == TYPEID_UNIT) { Creature* owner = (Creature*)m_sourceUnit; PathNode p = (distToStartPoly > 7.0f) ? startPos : endPos; if (m_sourceUnit->GetTerrain()->IsUnderWater(p.x, p.y, p.z)) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: underWater case\n"); if (owner->CanSwim()) buildShotrcut = true; } else { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: flying case\n"); if (owner->CanFly()) buildShotrcut = true; } } if (buildShotrcut) { BuildShortcut(); m_type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); return; } else { float closestPoint[VERTEX_SIZE]; // we may want to use closestPointOnPolyBoundary instead if (DT_SUCCESS == m_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint)) { dtVcopy(endPoint, closestPoint); setActualEndPosition(PathNode(endPoint[2],endPoint[0],endPoint[1])); } m_type = PATHFIND_INCOMPLETE; } } // *** poly path generating logic *** // start and end are on same polygon // just need to move in straight line if (startPoly == endPoly) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPoly == endPoly)\n"); BuildShortcut(); m_pathPolyRefs[0] = startPoly; m_polyLength = 1; m_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: path type %d\n", m_type); return; } // look for startPoly/endPoly in current path // TODO: we can merge it with getPathPolyByPosition() loop bool startPolyFound = false; bool endPolyFound = false; uint32 pathStartIndex, pathEndIndex; if (m_polyLength) { for (pathStartIndex = 0; pathStartIndex < m_polyLength; ++pathStartIndex) { // here to carch few bugs MANGOS_ASSERT(m_pathPolyRefs[pathStartIndex] != INVALID_POLYREF); if (m_pathPolyRefs[pathStartIndex] == startPoly) { startPolyFound = true; break; } } for (pathEndIndex = m_polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex) if (m_pathPolyRefs[pathEndIndex] == endPoly) { endPolyFound = true; break; } } if (startPolyFound && endPolyFound) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n"); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need // just "cut" it out m_polyLength = pathEndIndex - pathStartIndex + 1; memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, m_polyLength*sizeof(dtPolyRef)); } else if (startPolyFound && !endPolyFound) { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n"); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready m_polyLength -= pathStartIndex; // try to adjust the suffix of the path instead of recalculating entire length // at given interval the target cannot get too far from its last location // thus we have less poly to cover // sub-path of optimal path is optimal // take ~80% of the original length // TODO : play with the values here uint32 prefixPolyLength = uint32(m_polyLength*0.8f + 0.5f); memmove(m_pathPolyRefs, m_pathPolyRefs+pathStartIndex, prefixPolyLength*sizeof(dtPolyRef)); dtPolyRef suffixStartPoly = m_pathPolyRefs[prefixPolyLength-1]; // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data float suffixEndPoint[VERTEX_SIZE]; if (DT_SUCCESS != m_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint)) { // suffixStartPoly is invalid somehow, or the navmesh is broken => error state sLog.outError("%u's Path Build failed: invalid polyRef in path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } // generate suffix uint32 suffixPolyLength = 0; dtStatus dtResult = m_navMeshQuery->findPath( suffixStartPoly, // start polygon endPoly, // end polygon suffixEndPoint, // start position endPoint, // end position &m_filter, // polygon search filter m_pathPolyRefs + prefixPolyLength - 1, // [out] path (int*)&suffixPolyLength, MAX_PATH_LENGTH-prefixPolyLength); // max number of polygons in output path if (!suffixPolyLength || dtResult != DT_SUCCESS) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update // we still need to copy our preffix sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); } DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n",m_polyLength, prefixPolyLength, suffixPolyLength); // new path = prefix + suffix - overlap m_polyLength = prefixPolyLength + suffixPolyLength - 1; } else { DEBUG_FILTER_LOG(LOG_FILTER_PATHFINDING, "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n"); // either we have no path at all -> first run // or something went really wrong -> we aren't moving along the path to the target // just generate new path // free and invalidate old path data clear(); dtStatus dtResult = m_navMeshQuery->findPath( startPoly, // start polygon endPoly, // end polygon startPoint, // start position endPoint, // end position &m_filter, // polygon search filter m_pathPolyRefs, // [out] path (int*)&m_polyLength, MAX_PATH_LENGTH); // max number of polygons in output path if (!m_polyLength || dtResult != DT_SUCCESS) { // only happens if we passed bad data to findPath(), or navmesh is messed up sLog.outError("%u's Path Build failed: 0 length path", m_sourceUnit->GetGUID()); BuildShortcut(); m_type = PATHFIND_NOPATH; return; } } // by now we know what type of path we can get if (m_pathPolyRefs[m_polyLength - 1] == endPoly && !(m_type & PATHFIND_INCOMPLETE)) m_type = PATHFIND_NORMAL; else m_type = PATHFIND_INCOMPLETE; // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); }
bool WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) { if (!&creature) return true; // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff if (creature.hasUnitState(UNIT_STAT_NOT_MOVE)) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } // prevent a crash at empty waypoint path. if (!i_path || i_path->empty()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } if (i_currentNode >= i_path->size()) { sLog.outError("WaypointMovement currentNode (%u) is equal or bigger than path size (creature entry %u)", i_currentNode, creature.GetEntry()); i_currentNode = 0; } CreatureTraveller traveller(creature); i_nextMoveTime.Update(diff); if (i_destinationHolder.UpdateTraveller(traveller, diff, false, true)) { if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost } // creature has been stopped in middle of the waypoint segment if (!i_destinationHolder.HasArrived() && creature.IsStopped()) { // Timer has elapsed, meaning this part controlled it if (i_nextMoveTime.Passed()) { SetStoppedByPlayer(false); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.CanFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); // Now we re-set destination to same node and start travel const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else // if( !i_nextMoveTime.Passed()) { // unexpected end of timer && creature stopped && not at end of segment if (!IsStoppedByPlayer()) { // Put 30 seconds delay i_destinationHolder.IncreaseTravelTime(STOP_TIME_FOR_PLAYER); i_nextMoveTime.Reset(STOP_TIME_FOR_PLAYER); SetStoppedByPlayer(true); // Mark we did it } } return true; // Abort here this update } if (creature.IsStopped()) { if (!m_isArrivalDone) { if (i_path->at(i_currentNode).orientation != 100) creature.SetOrientation(i_path->at(i_currentNode).orientation); if (i_path->at(i_currentNode).script_id) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "Creature movement start script %u at point %u for creature %u (entry %u).", i_path->at(i_currentNode).script_id, i_currentNode, creature.GetDBTableGUIDLow(), creature.GetEntry()); creature.GetMap()->ScriptsStart(sCreatureMovementScripts, i_path->at(i_currentNode).script_id, &creature, &creature); } // We have reached the destination and can process behavior if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { if (behavior->emote != 0) creature.HandleEmote(behavior->emote); if (behavior->spell != 0) { creature.CastSpell(&creature, behavior->spell, false); if (!IsActive(creature)) // force stop processing (cast can change movegens list) return true; // not expire now, but already lost } if (behavior->model1 != 0) creature.SetDisplayId(behavior->model1); if (behavior->textid[0]) { // Not only one text is set if (behavior->textid[1]) { // Select one from max 5 texts (0 and 1 already checked) int i = 2; for(; i < MAX_WAYPOINT_TEXT; ++i) { if (!behavior->textid[i]) break; } creature.Say(behavior->textid[rand() % i], 0, 0); } else creature.Say(behavior->textid[0], 0, 0); } } // wpBehaviour found // Can only do this once for the node m_isArrivalDone = true; // Inform script MovementInform(creature); if (!IsActive(creature)) // force stop processing (movement can move out active zone with cleanup movegens list) return true; // not expire now, but already lost // prevent a crash at empty waypoint path. if (!i_path || i_path->empty() || i_currentNode >= i_path->size()) { creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); return true; } } } // i_creature.IsStopped() // This is at the end of waypoint segment (incl. was previously stopped by player, extending the time) if (i_nextMoveTime.Passed()) { // If stopped then begin a new move segment if (creature.IsStopped()) { creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (creature.CanFly()) creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); if (WaypointBehavior *behavior = i_path->at(i_currentNode).behavior) { if (behavior->model2 != 0) creature.SetDisplayId(behavior->model2); creature.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); } // behavior for "departure" of the current node is done m_isArrivalDone = false; // Proceed with increment current node and then send to the next destination ++i_currentNode; // Oops, end of the line so need to start from the beginning if (i_currentNode >= i_path->size()) i_currentNode = 0; if (i_path->at(i_currentNode).orientation != 100) creature.SetOrientation(i_path->at(i_currentNode).orientation); const WaypointNode &node = i_path->at(i_currentNode); i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z); i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); } else { // If not stopped then stop it creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); SetStoppedByPlayer(false); // Set TimeTracker to waittime for the current node i_nextMoveTime.Reset(i_path->at(i_currentNode).delay); } } return true; }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature& creature) { float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ; creature.GetHomePosition(respX, respY, respZ, respO); Map const* map = creature.GetBaseMap(); // For 2D/3D system selection //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<float>(M_PI*2.0f); const float range = float(rand_norm()) * wander_distance; const float distanceX = range * std::cos(angle); const float distanceY = range * std::sin(angle); destX = respX + distanceX; destY = respY + distanceY; // prevent invalid coordinates generation Trinity::NormalizeMapCoord(destX); Trinity::NormalizeMapCoord(destY); travelDistZ = distanceX*distanceX + distanceY*distanceY; 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; } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) travelDistZ = travelDistZ >= 100.0f ? 10.0f : sqrtf(travelDistZ); // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ+travelDistZ-2.0f, false); if (fabs(destZ - respZ) > travelDistZ) // Map check { // Vmap Horizontal or above destZ = map->GetHeight(creature.GetPhaseMask(), destX, destY, respZ - 2.0f, true); if (fabs(destZ - respZ) > travelDistZ) { // Vmap Higher 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) return; } } } if (is_air_ok) i_nextMoveTime.Reset(0); else 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.Launch(); //Call for creature group update if (creature.GetFormation() && creature.GetFormation()->getLeader() == &creature) creature.GetFormation()->LeaderMoveTo(destX, destY, destZ); }
void PathGenerator::BuildPolyPath(G3D::Vector3 const& startPos, G3D::Vector3 const& endPos) { // *** getting start/end poly logic *** float distToStartPoly, distToEndPoly; float startPoint[VERTEX_SIZE] = {startPos.y, startPos.z, startPos.x}; float endPoint[VERTEX_SIZE] = {endPos.y, endPos.z, endPos.x}; dtPolyRef startPoly = GetPolyByLocation(startPoint, &distToStartPoly); dtPolyRef endPoly = GetPolyByLocation(endPoint, &distToEndPoly); // we have a hole in our mesh // make shortcut path and mark it as NOPATH ( with flying and swimming exception ) // its up to caller how he will use this info if (startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF) { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)\n"); BuildShortcut(); bool path = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanFly(); bool waterPath = _sourceUnit->GetTypeId() == TYPEID_UNIT && _sourceUnit->ToCreature()->CanSwim(); if (waterPath) { // Check both start and end points, if they're both in water, then we can *safely* let the creature move for (uint32 i = 0; i < _pathPoints.size(); ++i) { ZLiquidStatus status = _sourceUnit->GetBaseMap()->getLiquidStatus(_pathPoints[i].x, _pathPoints[i].y, _pathPoints[i].z, MAP_ALL_LIQUIDS, NULL); // One of the points is not in the water, cancel movement. if (status == LIQUID_MAP_NO_WATER) { waterPath = false; break; } } } _type = (path || waterPath) ? PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH) : PATHFIND_NOPATH; return; } // we may need a better number here bool farFromPoly = (distToStartPoly > 7.0f || distToEndPoly > 7.0f); if (farFromPoly) { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f\n", distToStartPoly, distToEndPoly); bool buildShotrcut = false; if (_sourceUnit->GetTypeId() == TYPEID_UNIT) { Creature* owner = (Creature*)_sourceUnit; G3D::Vector3 const& p = (distToStartPoly > 7.0f) ? startPos : endPos; if (_sourceUnit->GetBaseMap()->IsUnderWater(p.x, p.y, p.z)) { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: underWater case\n"); if (owner->CanSwim()) buildShotrcut = true; } else { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: flying case\n"); if (owner->CanFly()) buildShotrcut = true; } } if (buildShotrcut) { BuildShortcut(); _type = PathType(PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH); return; } else { float closestPoint[VERTEX_SIZE]; // we may want to use closestPointOnPolyBoundary instead if (dtStatusSucceed(_navMeshQuery->closestPointOnPoly(endPoly, endPoint, closestPoint, NULL))) { dtVcopy(endPoint, closestPoint); SetActualEndPosition(G3D::Vector3(endPoint[2], endPoint[0], endPoint[1])); } _type = PATHFIND_INCOMPLETE; } } // *** poly path generating logic *** // start and end are on same polygon // just need to move in straight line if (startPoly == endPoly) { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPoly == endPoly)\n"); BuildShortcut(); _pathPolyRefs[0] = startPoly; _polyLength = 1; _type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL; TC_LOG_DEBUG("maps", "++ BuildPolyPath :: path type %d\n", _type); return; } // look for startPoly/endPoly in current path /// @todo we can merge it with getPathPolyByPosition() loop bool startPolyFound = false; bool endPolyFound = false; uint32 pathStartIndex = 0; uint32 pathEndIndex = 0; if (_polyLength) { for (; pathStartIndex < _polyLength; ++pathStartIndex) { // here to catch few bugs if (_pathPolyRefs[pathStartIndex] == INVALID_POLYREF) { TC_LOG_ERROR("maps", "Invalid poly ref in BuildPolyPath. _polyLength: %u, pathStartIndex: %u," " startPos: %s, endPos: %s, mapid: %u", _polyLength, pathStartIndex, startPos.toString().c_str(), endPos.toString().c_str(), _sourceUnit->GetMapId()); break; } if (_pathPolyRefs[pathStartIndex] == startPoly) { startPolyFound = true; break; } } for (pathEndIndex = _polyLength-1; pathEndIndex > pathStartIndex; --pathEndIndex) if (_pathPolyRefs[pathEndIndex] == endPoly) { endPolyFound = true; break; } } if (startPolyFound && endPolyFound) { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPolyFound && endPolyFound)\n"); // we moved along the path and the target did not move out of our old poly-path // our path is a simple subpath case, we have all the data we need // just "cut" it out _polyLength = pathEndIndex - pathStartIndex + 1; memmove(_pathPolyRefs, _pathPolyRefs + pathStartIndex, _polyLength * sizeof(dtPolyRef)); } else if (startPolyFound && !endPolyFound) { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (startPolyFound && !endPolyFound)\n"); // we are moving on the old path but target moved out // so we have atleast part of poly-path ready _polyLength -= pathStartIndex; // try to adjust the suffix of the path instead of recalculating entire length // at given interval the target cannot get too far from its last location // thus we have less poly to cover // sub-path of optimal path is optimal // take ~80% of the original length /// @todo play with the values here uint32 prefixPolyLength = uint32(_polyLength * 0.8f + 0.5f); memmove(_pathPolyRefs, _pathPolyRefs+pathStartIndex, prefixPolyLength * sizeof(dtPolyRef)); dtPolyRef suffixStartPoly = _pathPolyRefs[prefixPolyLength-1]; // we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data float suffixEndPoint[VERTEX_SIZE]; if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, NULL))) { // we can hit offmesh connection as last poly - closestPointOnPoly() don't like that // try to recover by using prev polyref --prefixPolyLength; suffixStartPoly = _pathPolyRefs[prefixPolyLength-1]; if (dtStatusFailed(_navMeshQuery->closestPointOnPoly(suffixStartPoly, endPoint, suffixEndPoint, NULL))) { // suffixStartPoly is still invalid, error state BuildShortcut(); _type = PATHFIND_NOPATH; return; } } // generate suffix uint32 suffixPolyLength = 0; dtStatus dtResult; if (_straightLine) { float hit = 0; float hitNormal[3]; memset(hitNormal, 0, sizeof(hitNormal)); dtResult = _navMeshQuery->raycast( suffixStartPoly, suffixEndPoint, endPoint, &_filter, &hit, hitNormal, _pathPolyRefs + prefixPolyLength - 1, (int*)&suffixPolyLength, MAX_PATH_LENGTH - prefixPolyLength); // raycast() sets hit to FLT_MAX if there is a ray between start and end if (hit != FLT_MAX) { // the ray hit something, return no path instead of the incomplete one _type = PATHFIND_NOPATH; return; } } else { dtResult = _navMeshQuery->findPath( suffixStartPoly, // start polygon endPoly, // end polygon suffixEndPoint, // start position endPoint, // end position &_filter, // polygon search filter _pathPolyRefs + prefixPolyLength - 1, // [out] path (int*)&suffixPolyLength, MAX_PATH_LENGTH - prefixPolyLength); // max number of polygons in output path } if (!suffixPolyLength || dtStatusFailed(dtResult)) { // this is probably an error state, but we'll leave it // and hopefully recover on the next Update // we still need to copy our preffix TC_LOG_ERROR("maps", "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow()); } TC_LOG_DEBUG("maps", "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u \n", _polyLength, prefixPolyLength, suffixPolyLength); // new path = prefix + suffix - overlap _polyLength = prefixPolyLength + suffixPolyLength - 1; } else { TC_LOG_DEBUG("maps", "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)\n"); // either we have no path at all -> first run // or something went really wrong -> we aren't moving along the path to the target // just generate new path // free and invalidate old path data Clear(); dtStatus dtResult; if (_straightLine) { float hit = 0; float hitNormal[3]; memset(hitNormal, 0, sizeof(hitNormal)); dtResult = _navMeshQuery->raycast( startPoly, startPoint, endPoint, &_filter, &hit, hitNormal, _pathPolyRefs, (int*)&_polyLength, MAX_PATH_LENGTH); // raycast() sets hit to FLT_MAX if there is a ray between start and end if (hit != FLT_MAX) { // the ray hit something, return no path instead of the incomplete one _type = PATHFIND_NOPATH; return; } } else { dtResult = _navMeshQuery->findPath( startPoly, // start polygon endPoly, // end polygon startPoint, // start position endPoint, // end position &_filter, // polygon search filter _pathPolyRefs, // [out] path (int*)&_polyLength, MAX_PATH_LENGTH); // max number of polygons in output path } if (!_polyLength || dtStatusFailed(dtResult)) { // only happens if we passed bad data to findPath(), or navmesh is messed up TC_LOG_ERROR("maps", "%u's Path Build failed: 0 length path", _sourceUnit->GetGUIDLow()); BuildShortcut(); _type = PATHFIND_NOPATH; return; } } // by now we know what type of path we can get if (_pathPolyRefs[_polyLength - 1] == endPoly && !(_type & PATHFIND_INCOMPLETE)) _type = PATHFIND_NORMAL; else _type = PATHFIND_INCOMPLETE; // generate the point-path out of our up-to-date poly-path BuildPointPath(startPoint, endPoint); }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float respX, respY, respZ, respO, currZ, destX, destY, destZ, wander_distance, travelDistZ; creature.GetRespawnCoord(respX, respY, respZ, &respO, &wander_distance); currZ = creature.GetPositionZ(); TerrainInfo const* map = creature.GetTerrain(); // For 2D/3D system selection //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 = rand_norm_f() * (M_PI_F*2.0f); const float range = rand_norm_f() * wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); destX = respX + distanceX; destY = respY + distanceY; // prevent invalid coordinates generation MaNGOS::NormalizeMapCoord(destX); MaNGOS::NormalizeMapCoord(destY); travelDistZ = distanceX*distanceX + distanceY*distanceY; if (is_air_ok) // 3D system above ground and above water (flying mode) { // Limit height change const float distanceZ = rand_norm_f() * 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; } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { destZ = respZ; if(!map->IsNextZcoordOK(destX, destY, destZ, travelDistZ)) return; // let's forget this bad coords where a z cannot be find and retry at next tick creature.UpdateGroundPositionZ(destX, destY, destZ, travelDistZ); } Traveller<Creature> traveller(creature); creature.SetOrientation(creature.GetAngle(destX, destY)); i_destinationHolder.SetDestination(traveller, destX, destY, destZ); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (is_air_ok) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); creature.AddSplineFlag(SPLINEFLAG_UNKNOWN7); } //else if (is_water_ok) // Swimming mode to be done with more than this check else { i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(), 10000+i_destinationHolder.GetTotalTravelTime())); creature.AddSplineFlag(SPLINEFLAG_WALKMODE); } }
void RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) { float respX, respY, respZ, respO, currZ, destX, destY, destZ, wander_distance, travelDistZ; creature.GetRespawnCoord(respX, respY, respZ, &respO, &wander_distance); currZ = creature.GetPositionZ(); TerrainInfo const* map = creature.GetTerrain(); // For 2D/3D system selection //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 = rand_norm_f() * (M_PI_F*2.0f); const float range = rand_norm_f() * wander_distance; const float distanceX = range * cos(angle); const float distanceY = range * sin(angle); destX = respX + distanceX; destY = respY + distanceY; // prevent invalid coordinates generation MaNGOS::NormalizeMapCoord(destX); MaNGOS::NormalizeMapCoord(destY); travelDistZ = distanceX*distanceX + distanceY*distanceY; if (is_air_ok) // 3D system above ground and above water (flying mode) { // Limit height change const float distanceZ = rand_norm_f() * 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; } //else if (is_water_ok) // 3D system under water and above ground (swimming mode) else // 2D only { // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) travelDistZ = travelDistZ >= 100.0f ? 10.0f : sqrtf(travelDistZ); // The fastest way to get an accurate result 90% of the time. // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. destZ = map->GetHeight(destX, destY, respZ+travelDistZ-2.0f, false); if (fabs(destZ - respZ) > travelDistZ) // Map check { // Vmap Horizontal or above destZ = map->GetHeight(destX, destY, respZ - 2.0f, true); if (fabs(destZ - respZ) > travelDistZ) { // Vmap Higher destZ = map->GetHeight(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) return; } } } Traveller<Creature> traveller(creature); creature.SetOrientation(creature.GetAngle(destX, destY)); i_destinationHolder.SetDestination(traveller, destX, destY, destZ); creature.addUnitState(UNIT_STAT_ROAMING_MOVE); if (is_air_ok) { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); creature.AddSplineFlag(SPLINEFLAG_FLYING); } //else if (is_water_ok) // Swimming mode to be done with more than this check else { i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime() + urand(500, 10000)); creature.AddSplineFlag(SPLINEFLAG_WALKMODE); } }