public void LoadTransportTemplates() { uint oldMSTime = Time.GetMSTime(); SQLResult result = DB.World.Query("SELECT entry FROM gameobject_template WHERE type = 15 ORDER BY entry ASC"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 transports templates. DB table `gameobject_template` has no transports!"); return; } uint count = 0; do { uint entry = result.Read <uint>(0); GameObjectTemplate goInfo = Global.ObjectMgr.GetGameObjectTemplate(entry); if (goInfo == null) { Log.outError(LogFilter.Sql, "Transport {0} has no associated GameObjectTemplate from `gameobject_template` , skipped.", entry); continue; } if (!CliDB.TaxiPathNodesByPath.ContainsKey(goInfo.MoTransport.taxiPathID)) { Log.outError(LogFilter.Sql, "Transport {0} (name: {1}) has an invalid path specified in `gameobject_template`.`data0` ({2}) field, skipped.", entry, goInfo.name, goInfo.MoTransport.taxiPathID); continue; } if (goInfo.MoTransport.taxiPathID == 0) { continue; } // paths are generated per template, saves us from generating it again in case of instanced transports TransportTemplate transport = new TransportTemplate(); transport.entry = entry; GeneratePath(goInfo, transport); _transportTemplates[entry] = transport; // transports in instance are only on one map if (transport.inInstance) { _instanceTransports.Add(transport.mapsUsed.First(), entry); } ++count; } while (result.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} transports in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); }
void GeneratePath(GameObjectTemplate goInfo, TransportTemplate transport) { uint pathId = goInfo.MoTransport.taxiPathID; var path = CliDB.TaxiPathNodesByPath[pathId]; List <KeyFrame> keyFrames = transport.keyFrames; List <Vector3> splinePath = new(); List <Vector3> allPoints = new(); bool mapChange = false; for (uint i = 0; i < path.Length; ++i) { allPoints.Add(new Vector3(path[i].Loc.X, path[i].Loc.Y, path[i].Loc.Z)); } // Add extra points to allow derivative calculations for all path nodes allPoints.Insert(0, allPoints.First().lerp(allPoints[1], -0.2f)); allPoints.Add(allPoints.Last().lerp(allPoints[^ 2], -0.2f));
public void SpawnContinentTransports() { if (_transportTemplates.Empty()) { return; } uint oldMSTime = Time.GetMSTime(); SQLResult result = DB.World.Query("SELECT guid, entry, phaseid, phasegroup FROM transports"); uint count = 0; if (!result.IsEmpty()) { do { ulong guid = result.Read <ulong>(0); uint entry = result.Read <uint>(1); uint phaseid = result.Read <uint>(2); uint phasegroup = result.Read <uint>(3); TransportTemplate tInfo = GetTransportTemplate(entry); if (tInfo != null) { if (!tInfo.inInstance) { if (CreateTransport(entry, guid, null, phaseid, phasegroup)) { ++count; } } } } while (result.NextRow()); } Log.outInfo(LogFilter.ServerLoading, "Spawned {0} continent transports in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); }
public void SpawnContinentTransports() { if (_transportTemplates.Empty()) { return; } uint oldMSTime = Time.GetMSTime(); SQLResult result = DB.World.Query("SELECT guid, entry, phaseUseFlags, phaseid, phasegroup FROM transports"); uint count = 0; if (!result.IsEmpty()) { do { ulong guid = result.Read <ulong>(0); uint entry = result.Read <uint>(1); PhaseUseFlagsValues phaseUseFlags = (PhaseUseFlagsValues)result.Read <byte>(2); uint phaseId = result.Read <uint>(3); uint phaseGroupId = result.Read <uint>(4); if (Convert.ToBoolean(phaseUseFlags & ~PhaseUseFlagsValues.All)) { Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with unknown `phaseUseFlags` set, removed unknown value."); phaseUseFlags &= PhaseUseFlagsValues.All; } if (phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.AlwaysVisible) && phaseUseFlags.HasAnyFlag(PhaseUseFlagsValues.Inverse)) { Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE," + " removing PHASE_USE_FLAGS_INVERSE."); phaseUseFlags &= ~PhaseUseFlagsValues.Inverse; } if (phaseGroupId != 0 && phaseId != 0) { Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0"); phaseGroupId = 0; } if (phaseId != 0) { if (!CliDB.PhaseStorage.ContainsKey(phaseId)) { Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with `phaseid` {phaseId} does not exist, set to 0"); phaseId = 0; } } if (phaseGroupId != 0) { if (Global.DB2Mgr.GetPhasesForGroup(phaseGroupId).Empty()) { Log.outError(LogFilter.Sql, $"Table `transports` have transport (GUID: {guid} Entry: {entry}) with `phaseGroup` {phaseGroupId} does not exist, set to 0"); phaseGroupId = 0; } } TransportTemplate tInfo = GetTransportTemplate(entry); if (tInfo != null) { if (!tInfo.inInstance) { if (CreateTransport(entry, guid, null, phaseUseFlags, phaseId, phaseGroupId)) { ++count; } } } } while (result.NextRow()); } Log.outInfo(LogFilter.ServerLoading, "Spawned {0} continent transports in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); }
public Transport CreateTransport(uint entry, ulong guid = 0, Map map = null, PhaseUseFlagsValues phaseUseFlags = 0, uint phaseId = 0, uint phaseGroupId = 0) { // instance case, execute GetGameObjectEntry hook if (map != null) { // SetZoneScript() is called after adding to map, so fetch the script using map if (map.IsDungeon()) { InstanceScript instance = ((InstanceMap)map).GetInstanceScript(); if (instance != null) { entry = instance.GetGameObjectEntry(0, entry); } } if (entry == 0) { return(null); } } TransportTemplate tInfo = GetTransportTemplate(entry); if (tInfo == null) { Log.outError(LogFilter.Sql, "Transport {0} will not be loaded, `transport_template` missing", entry); return(null); } // create transport... Transport trans = new(); // ...at first waypoint TaxiPathNodeRecord startNode = tInfo.keyFrames.First().Node; uint mapId = startNode.ContinentID; float x = startNode.Loc.X; float y = startNode.Loc.Y; float z = startNode.Loc.Z; float o = tInfo.keyFrames.First().InitialOrientation; // initialize the gameobject base ulong guidLow = guid != 0 ? guid : map.GenerateLowGuid(HighGuid.Transport); if (!trans.Create(guidLow, entry, mapId, x, y, z, o, 255)) { return(null); } PhasingHandler.InitDbPhaseShift(trans.GetPhaseShift(), phaseUseFlags, phaseId, phaseGroupId); MapRecord mapEntry = CliDB.MapStorage.LookupByKey(mapId); if (mapEntry != null) { if (mapEntry.Instanceable() != tInfo.inInstance) { Log.outError(LogFilter.Transport, "Transport {0} (name: {1}) attempted creation in instance map (id: {2}) but it is not an instanced transport!", entry, trans.GetName(), mapId); //return null; } } // use preset map for instances (need to know which instance) trans.SetMap(map != null ? map : Global.MapMgr.CreateMap(mapId, null)); if (map != null && map.IsDungeon()) { trans.m_zoneScript = map.ToInstanceMap().GetInstanceScript(); } // Passengers will be loaded once a player is near Global.ObjAccessor.AddObject(trans); trans.GetMap().AddToMap(trans); return(trans); }
void GeneratePath(GameObjectTemplate goInfo, TransportTemplate transport) { uint pathId = goInfo.MoTransport.taxiPathID; var path = CliDB.TaxiPathNodesByPath[pathId]; List <KeyFrame> keyFrames = transport.keyFrames; List <Vector3> splinePath = new(); List <Vector3> allPoints = new(); bool mapChange = false; for (uint i = 0; i < path.Length; ++i) { allPoints.Add(new Vector3(path[i].Loc.X, path[i].Loc.Y, path[i].Loc.Z)); } // Add extra points to allow derivative calculations for all path nodes allPoints.Insert(0, allPoints.First().lerp(allPoints[1], -0.2f)); allPoints.Add(allPoints.Last().lerp(allPoints[allPoints.Count - 2], -0.2f)); allPoints.Add(allPoints.Last().lerp(allPoints[allPoints.Count - 2], -1.0f)); SplineRawInitializer initer = new(allPoints); Spline orientationSpline = new(); orientationSpline.InitSplineCustom(initer); orientationSpline.InitLengths(); for (uint i = 0; i < path.Length; ++i) { if (!mapChange) { var node_i = path[i]; if (i != path.Length - 1 && (node_i.Flags.HasAnyFlag(TaxiPathNodeFlags.Teleport) || node_i.ContinentID != path[i + 1].ContinentID)) { keyFrames.Last().Teleport = true; mapChange = true; } else { KeyFrame k = new(node_i); Vector3 h; orientationSpline.Evaluate_Derivative((int)(i + 1), 0.0f, out h); k.InitialOrientation = Position.NormalizeOrientation((float)Math.Atan2(h.Y, h.X) + MathFunctions.PI); keyFrames.Add(k); splinePath.Add(new Vector3(node_i.Loc.X, node_i.Loc.Y, node_i.Loc.Z)); if (!transport.mapsUsed.Contains(k.Node.ContinentID)) { transport.mapsUsed.Add(k.Node.ContinentID); } } } else { mapChange = false; } } if (splinePath.Count >= 2) { // Remove special catmull-rom spline points if (!keyFrames.First().IsStopFrame() && keyFrames.First().Node.ArrivalEventID == 0 && keyFrames.First().Node.DepartureEventID == 0) { splinePath.RemoveAt(0); keyFrames.RemoveAt(0); } if (!keyFrames.Last().IsStopFrame() && keyFrames.Last().Node.ArrivalEventID == 0 && keyFrames.Last().Node.DepartureEventID == 0) { splinePath.RemoveAt(splinePath.Count - 1); keyFrames.RemoveAt(keyFrames.Count - 1); } } Cypher.Assert(!keyFrames.Empty()); if (transport.mapsUsed.Count > 1) { foreach (var mapId in transport.mapsUsed) { Cypher.Assert(!CliDB.MapStorage.LookupByKey(mapId).Instanceable()); } transport.inInstance = false; } else { transport.inInstance = CliDB.MapStorage.LookupByKey(transport.mapsUsed.First()).Instanceable(); } // last to first is always "teleport", even for closed paths keyFrames.Last().Teleport = true; float speed = goInfo.MoTransport.moveSpeed; float accel = goInfo.MoTransport.accelRate; float accel_dist = 0.5f * speed * speed / accel; transport.accelTime = speed / accel; transport.accelDist = accel_dist; int firstStop = -1; int 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 int start = 0; for (int i = 1; i < keyFrames.Count; ++i) { if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.Count) { int extra = !keyFrames[i - 1].Teleport ? 1 : 0; Spline spline = new(); Span <Vector3> span = splinePath.ToArray(); spline.InitSpline(span.Slice(start), i - start + extra, Spline.EvaluationMode.Catmullrom); spline.InitLengths(); for (int j = start; j < i + extra; ++j) { keyFrames[j].Index = (uint)(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 = (uint)(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.Last().NextDistFromPrev = keyFrames.First().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 (int i = 0; i < keyFrames.Count; ++i) { int j = (i + lastStop) % keyFrames.Count; if (keyFrames[j].IsStopFrame() || j == lastStop) { tmpDist = 0.0f; } else { tmpDist += keyFrames[j].DistFromPrev; } keyFrames[j].DistSinceStop = tmpDist; } tmpDist = 0.0f; for (int i = (keyFrames.Count - 1); i >= 0; i--) { int j = (i + firstStop) % keyFrames.Count; tmpDist += keyFrames[(j + 1) % keyFrames.Count].DistFromPrev; keyFrames[j].DistUntilStop = tmpDist; if (keyFrames[j].IsStopFrame() || j == firstStop) { tmpDist = 0.0f; } } for (int i = 0; i < keyFrames.Count; ++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 * (float)Math.Sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel); // substract acceleration time keyFrames[i].TimeTo = segment_time - (float)Math.Sqrt(2 * keyFrames[i].DistSinceStop / accel); } else // slowing down { keyFrames[i].TimeTo = (float)Math.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 - (float)Math.Sqrt(2 * keyFrames[i].DistSinceStop / accel); } else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed) { keyFrames[i].TimeTo = (float)Math.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 (int i = 0; i < keyFrames.Count; ++i) { int j = (i + lastStop) % keyFrames.Count; 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 = keyFrames[0].Node.Delay; keyFrames[0].DepartureTime = (uint)(curPathTime * Time.InMilliseconds); } for (int i = 1; i < keyFrames.Count; ++i) { curPathTime += keyFrames[i - 1].TimeTo; if (keyFrames[i].IsStopFrame()) { keyFrames[i].ArriveTime = (uint)(curPathTime * Time.InMilliseconds); keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; curPathTime += keyFrames[i].Node.Delay; keyFrames[i].DepartureTime = (uint)(curPathTime * Time.InMilliseconds); } else { curPathTime -= keyFrames[i].TimeTo; keyFrames[i].ArriveTime = (uint)(curPathTime * Time.InMilliseconds); keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; keyFrames[i].DepartureTime = keyFrames[i].ArriveTime; } } keyFrames.Last().NextArriveTime = keyFrames.Last().DepartureTime; transport.pathTime = keyFrames.Last().DepartureTime; if (transport.pathTime == 0) { } }