void operator()(uint8& mode, bool& cyclic, Movement::PointsArray& points, int& lo, int& hi) const { mode = Movement::SplineBase::ModeCatmullrom; cyclic = false; points.assign(_points.begin(), _points.end()); lo = 1; hi = points.size() - 2; }
uint32 SplineChainMovementGenerator::SendPathSpline(Unit* me, Movement::PointsArray const& wp) const { uint32 numWp = wp.size(); ASSERT(numWp > 1 && "Every path must have source & destination"); Movement::MoveSplineInit init(me); if (numWp > 2) init.MovebyPath(wp); else init.MoveTo(wp[1], false, true); init.SetWalk(_walk); return init.Launch(); }
uint32 SplineChainMovementGenerator::SendPathSpline(Unit* owner, Movement::PointsArray const& path) const { uint32 nodeCount = path.size(); ASSERT(nodeCount > 1, "SplineChainMovementGenerator::SendPathSpline: Every path must have source & destination (size > 1)! (%s)", owner->GetGUID().ToString().c_str()); Movement::MoveSplineInit init(owner); if (nodeCount > 2) init.MovebyPath(path); else init.MoveTo(path[1], false, true); init.SetWalk(_walk); return init.Launch(); }
void TransportMgr::GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport) { uint32 pathId = goInfo->moTransport.taxiPathId; TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId]; std::vector<KeyFrame>& keyFrames = transport->keyFrames; Movement::PointsArray splinePath; bool mapChange = false; bool cyclic = true; for (size_t i = 0; i < path.size(); ++i) { if (!mapChange) { TaxiPathNodeEntry const& node_i = path[i]; if (i != path.size() - 1 && (node_i.actionFlag == 1 || node_i.mapid != path[i + 1].mapid)) { cyclic = false; keyFrames.back().Teleport = true; mapChange = true; } else { KeyFrame k(node_i); keyFrames.push_back(k); splinePath.push_back(G3D::Vector3(node_i.x, node_i.y, node_i.z)); transport->mapsUsed.insert(k.Node->mapid); } } else mapChange = false; } // Not sure if data8 means the transport can be stopped or that its path in dbc does not contain extra spline points if (!goInfo->moTransport.canBeStopped && splinePath.size() >= 2) { // Remove special catmull-rom spline points splinePath.erase(splinePath.begin()); keyFrames.erase(keyFrames.begin()); splinePath.pop_back(); keyFrames.pop_back(); // Cyclic spline has one more extra point if (cyclic && !splinePath.empty()) { splinePath.pop_back(); keyFrames.pop_back(); } } ASSERT(!keyFrames.empty()); if (transport->mapsUsed.size() > 1) { for (std::set<uint32>::const_iterator itr = transport->mapsUsed.begin(); itr != transport->mapsUsed.end(); ++itr) ASSERT(!sMapStore.LookupEntry(*itr)->Instanceable()); transport->inInstance = false; } else transport->inInstance = sMapStore.LookupEntry(*transport->mapsUsed.begin())->Instanceable(); // last to first is always "teleport", even for closed paths keyFrames.back().Teleport = true; const float speed = float(goInfo->moTransport.moveSpeed); const float accel = float(goInfo->moTransport.accelRate); const float accel_dist = 0.5f * speed * speed / accel; transport->accelTime = speed / accel; transport->accelDist = accel_dist; int32 firstStop = -1; int32 lastStop = -1; // first cell is arrived at by teleportation :S keyFrames[0].DistFromPrev = 0; keyFrames[0].Index = 1; if (keyFrames[0].IsStopFrame()) { firstStop = 0; lastStop = 0; } // find the rest of the distances between key points // Every path segment has its own spline if (cyclic) { TransportSpline* spline = new TransportSpline(); spline->init_cyclic_spline(&splinePath[0], splinePath.size(), Movement::SplineBase::ModeCatmullrom, 0); spline->initLengths(); keyFrames[0].DistFromPrev = spline->length(spline->last() - 2, spline->last() - 1); keyFrames[0].Spline = spline; for (size_t i = 0; i < keyFrames.size(); ++i) { keyFrames[i].Index = i + 1; keyFrames[i].DistFromPrev = spline->length(i, i + 1); if (i > 0) keyFrames[i - 1].NextDistFromPrev = keyFrames[i].DistFromPrev; keyFrames[i].Spline = spline; if (keyFrames[i].IsStopFrame()) { // remember first stop frame if (firstStop == -1) firstStop = i; lastStop = i; } } } else { size_t start = 0; for (size_t i = 1; i < keyFrames.size(); ++i) { if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.size()) { size_t extra = !keyFrames[i - 1].Teleport ? 1 : 0; TransportSpline* spline = new TransportSpline(); spline->init_spline(&splinePath[start], i - start + extra, Movement::SplineBase::ModeCatmullrom); spline->initLengths(); for (size_t j = start; j < i + extra; ++j) { keyFrames[j].Index = j - start + 1; keyFrames[j].DistFromPrev = spline->length(j - start, j + 1 - start); if (j > 0) keyFrames[j - 1].NextDistFromPrev = keyFrames[j].DistFromPrev; keyFrames[j].Spline = spline; } if (keyFrames[i - 1].Teleport) { keyFrames[i].Index = i - start + 1; keyFrames[i].DistFromPrev = 0.0f; keyFrames[i - 1].NextDistFromPrev = 0.0f; keyFrames[i].Spline = spline; } start = i; } if (keyFrames[i].IsStopFrame()) { // remember first stop frame if (firstStop == -1) firstStop = i; lastStop = i; } } } keyFrames.back().NextDistFromPrev = keyFrames.front().DistFromPrev; if (firstStop == -1 || lastStop == -1) firstStop = lastStop = 0; // at stopping keyframes, we define distSinceStop == 0, // and distUntilStop is to the next stopping keyframe. // this is required to properly handle cases of two stopping frames in a row (yes they do exist) float tmpDist = 0.0f; for (size_t i = 0; i < keyFrames.size(); ++i) { int32 j = (i + lastStop) % keyFrames.size(); if (keyFrames[j].IsStopFrame() || j == lastStop) tmpDist = 0.0f; else tmpDist += keyFrames[j].DistFromPrev; keyFrames[j].DistSinceStop = tmpDist; } tmpDist = 0.0f; for (int32 i = int32(keyFrames.size()) - 1; i >= 0; i--) { int32 j = (i + firstStop) % keyFrames.size(); tmpDist += keyFrames[(j + 1) % keyFrames.size()].DistFromPrev; keyFrames[j].DistUntilStop = tmpDist; if (keyFrames[j].IsStopFrame() || j == firstStop) tmpDist = 0.0f; } for (size_t i = 0; i < keyFrames.size(); ++i) { float total_dist = keyFrames[i].DistSinceStop + keyFrames[i].DistUntilStop; if (total_dist < 2 * accel_dist) // won't reach full speed { if (keyFrames[i].DistSinceStop < keyFrames[i].DistUntilStop) // is still accelerating { // calculate accel+brake time for this short segment float segment_time = 2.0f * sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel); // substract acceleration time keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel); } else // slowing down keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel); } else if (keyFrames[i].DistSinceStop < accel_dist) // still accelerating (but will reach full speed) { // calculate accel + cruise + brake time for this long segment float segment_time = (keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / speed + (speed / accel); // substract acceleration time keyFrames[i].TimeTo = segment_time - sqrt(2 * keyFrames[i].DistSinceStop / accel); } else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed) keyFrames[i].TimeTo = sqrt(2 * keyFrames[i].DistUntilStop / accel); else // at full speed keyFrames[i].TimeTo = (keyFrames[i].DistUntilStop / speed) + (0.5f * speed / accel); } // calculate tFrom times from tTo times float segmentTime = 0.0f; for (size_t i = 0; i < keyFrames.size(); ++i) { int32 j = (i + lastStop) % keyFrames.size(); if (keyFrames[j].IsStopFrame() || j == lastStop) segmentTime = keyFrames[j].TimeTo; keyFrames[j].TimeFrom = segmentTime - keyFrames[j].TimeTo; } // calculate path times keyFrames[0].ArriveTime = 0; float curPathTime = 0.0f; if (keyFrames[0].IsStopFrame()) { curPathTime = float(keyFrames[0].Node->delay); keyFrames[0].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); } for (size_t i = 1; i < keyFrames.size(); ++i) { curPathTime += keyFrames[i - 1].TimeTo; if (keyFrames[i].IsStopFrame()) { keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; curPathTime += float(keyFrames[i].Node->delay); keyFrames[i].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); } else { curPathTime -= keyFrames[i].TimeTo; keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; keyFrames[i].DepartureTime = keyFrames[i].ArriveTime; } } keyFrames.back().NextArriveTime = keyFrames.back().DepartureTime; transport->pathTime = keyFrames.back().DepartureTime; }
bool WaypointMovementGenerator<Creature>::StartMove(Creature* creature) { if (!creature || !creature->IsAlive()) return false; if (!i_path || i_path->nodes.empty()) return false; bool transportPath = creature->GetTransport() != nullptr; if (IsArrivalDone) { if ((i_currentNode == i_path->nodes.size() - 1) && !repeating) // If that's our last waypoint { WaypointNode const &waypoint = i_path->nodes.at(i_currentNode); float x = waypoint.x; float y = waypoint.y; float z = waypoint.z; float o = creature->GetOrientation(); if (!transportPath) creature->SetHomePosition(x, y, z, o); else { if (Transport* trans = creature->GetTransport()) { o -= trans->GetOrientation(); creature->SetTransportHomePosition(x, y, z, o); trans->CalculatePassengerPosition(x, y, z, &o); creature->SetHomePosition(x, y, z, o); } else transportPath = false; // else if (vehicle) - this should never happen, vehicle offsets are const } return false; } i_currentNode = (i_currentNode + 1) % i_path->nodes.size(); } float finalOrient = 0.0f; uint8 finalMove = WAYPOINT_MOVE_TYPE_WALK; Movement::PointsArray pathing; pathing.reserve((i_path->nodes.size() - i_currentNode) + 1); pathing.push_back(G3D::Vector3(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ())); for (uint32 i = i_currentNode; i < i_path->nodes.size(); ++i) { WaypointNode const &waypoint = i_path->nodes.at(i); pathing.push_back(G3D::Vector3(waypoint.x, waypoint.y, waypoint.z)); finalOrient = waypoint.orientation; finalMove = waypoint.moveType; if (waypoint.delay) break; } // if we have only 1 point, only current position, we shall return if (pathing.size() < 2) return false; IsArrivalDone = false; i_recalculateSpeed = false; creature->AddUnitState(UNIT_STATE_ROAMING_MOVE); Movement::MoveSplineInit init(creature); Movement::Location formationDest(i_path->nodes.at(i_currentNode).x, i_path->nodes.at(i_currentNode).y, i_path->nodes.at(i_currentNode).z, 0.0f); //! If creature is on transport, we assume waypoints set in DB are already transport offsets if (transportPath) { init.DisableTransportPathTransformations(); if (TransportBase* trans = creature->GetDirectTransport()) trans->CalculatePassengerPosition(formationDest.x, formationDest.y, formationDest.z, &formationDest.orientation); } init.MovebyPath(pathing, i_currentNode); switch (finalMove) { case WAYPOINT_MOVE_TYPE_LAND: init.SetAnimation(Movement::ToGround); break; case WAYPOINT_MOVE_TYPE_TAKEOFF: init.SetAnimation(Movement::ToFly); break; case WAYPOINT_MOVE_TYPE_RUN: init.SetWalk(false); break; case WAYPOINT_MOVE_TYPE_WALK: init.SetWalk(true); break; } if (finalOrient != 0.0f) init.SetFacing(finalOrient); init.Launch(); //Call for creature group update if (creature->GetFormation() && creature->GetFormation()->getLeader() == creature) creature->GetFormation()->LeaderMoveTo(formationDest.x, formationDest.y, formationDest.z); return true; }