public static void MatchVerts(Mesh mesh, PQS pqs, double oceanHeight, float scaleFactor) { ProfileTimer.Push("MatchVerts"); if (pqs == null) { MonoBehaviour.print("ERROR matching verts: pqs is null!"); return; } char sep = System.IO.Path.DirectorySeparatorChar; pqs.isBuildingMaps = true; Vector3[] vertices = new Vector3[mesh.vertexCount]; for (int i = 0; i < mesh.vertexCount; i++) { Vector3 v = mesh.vertices[i]; double height = pqs.GetSurfaceHeight(v); if (height < oceanHeight) { height = oceanHeight; } vertices[i] = v.normalized * (float)(1000.0 * height / pqs.radius * scaleFactor); } pqs.isBuildingMaps = false; mesh.vertices = vertices; ProfileTimer.Pop("MatchVerts"); }
private Single GetAltitudeFromOcean(Vector3 position) { PQS ocean = GetOcean(); return((Single)ocean.GetSurfaceHeight(ocean.GetRelativePosition(position)) - (Single)Part.vessel.mainBody.Radius); }
protected Vector3 getVesselPosition() { PQS pqs = this.part.vessel.mainBody.pqsController; double alt = pqs.GetSurfaceHeight(vessel.mainBody.GetRelSurfaceNVector(this.part.vessel.latitude, this.part.vessel.longitude)) - vessel.mainBody.Radius; alt = Math.Max(alt, 0); // Underwater! return(this.part.vessel.mainBody.GetRelSurfacePosition(this.part.vessel.latitude, this.part.vessel.longitude, alt)); }
// return terrain height at point specified // - body terrain must be loaded for this to work: use it only for loaded vessels public static double TerrainHeight(CelestialBody body, Vector3d pos) { PQS pqs = body.pqsController; if (pqs == null) return 0.0; Vector2d latlong = body.GetLatitudeAndLongitude(pos); Vector3d radial = QuaternionD.AngleAxis(latlong.y, Vector3d.down) * QuaternionD.AngleAxis(latlong.x, Vector3d.forward) * Vector3d.right; return (pos - body.position).magnitude - pqs.GetSurfaceHeight(radial); }
/// <summary> /// Returns the ground's altitude above sea level at this geo position. /// </summary> /// <returns></returns> /// <remarks>Borrowed this from the kOS mod with slight modification</remarks> /// <see cref="https://github.com/KSP-KOS/KOS/blob/develop/src/kOS/Suffixed/GeoCoordinates.cs"/> public Double GetTerrainAltitude() { double alt = 0.0; PQS bodyPQS = Body.pqsController; if (bodyPQS != null) // The sun has no terrain. Everything else has a PQScontroller. { // The PQS controller gives the theoretical ideal smooth surface curve terrain. // The actual ground that exists in-game that you land on, however, is the terrain // polygon mesh which is built dynamically from the PQS controller's altitude values, // and it only approximates the PQS controller. The discrepancy between the two // can be as high as 20 meters on relatively mild rolling terrain and is probably worse // in mountainous terrain with steeper slopes. It also varies with the user terrain detail // graphics setting. // Therefore the algorithm here is this: Get the PQS ideal terrain altitude first. // Then try using RayCast to get the actual terrain altitude, which will only work // if the LAT/LONG is near the active vessel so the relevant terrain polygons are // loaded. If the RayCast hit works, it overrides the PQS altitude. // PQS controller ideal altitude value: // ------------------------------------- // The vector the pqs GetSurfaceHeight method expects is a vector in the following // reference frame: // Origin = body center. // X axis = LATLNG(0,0), Y axis = LATLNG(90,0)(north pole), Z axis = LATLNG(0,-90). // Using that reference frame, you tell GetSurfaceHeight what the "up" vector is pointing through // the spot on the surface you're querying for. var bodyUpVector = new Vector3d(1, 0, 0); bodyUpVector = QuaternionD.AngleAxis(Latitude, Vector3d.forward /*around Z axis*/) * bodyUpVector; bodyUpVector = QuaternionD.AngleAxis(Longitude, Vector3d.down /*around -Y axis*/) * bodyUpVector; alt = bodyPQS.GetSurfaceHeight(bodyUpVector) - bodyPQS.radius; // Terrain polygon raycasting: // --------------------------- const double HIGH_AGL = 1000.0; const double POINT_AGL = 800.0; const int TERRAIN_MASK_BIT = 15; // a point hopefully above the terrain: Vector3d worldRayCastStart = Body.GetWorldSurfacePosition(Latitude, Longitude, alt + HIGH_AGL); // a point a bit below it, to aim down to the terrain: Vector3d worldRayCastStop = Body.GetWorldSurfacePosition(Latitude, Longitude, alt + POINT_AGL); RaycastHit hit; if (Physics.Raycast(worldRayCastStart, (worldRayCastStop - worldRayCastStart), out hit, float.MaxValue, 1 << TERRAIN_MASK_BIT)) { // Ensure hit is on the topside of planet, near the worldRayCastStart, not on the far side. if (Mathf.Abs(hit.distance) < 3000) { // Okay a hit was found, use it instead of PQS alt: alt = ((alt + HIGH_AGL) - hit.distance); } } } return(alt); }
/// <summary> /// Get the altitude of the surface at a point directly under where the vessel will be at /// the specified time. /// </summary> /// <param name="vessel"></param> /// <param name="universalTime"></param> /// <param name="currentUniversalTime"></param> /// <returns></returns> private static double SurfaceAltitudeAtTime(Vessel vessel, double universalTime, double currentUniversalTime) { // Note that the actual *exact* surface height will be different from what this function returns. // That's because this function simply uses the PQS controller, which gives a theoretical ideal // smooth-surface-curve terrain. The actual game adds polygons into the mix, which can throw the // number off by 20 meters or more. Useful discussion (and code to work around it) is here, // in the GetTerrainAltitude function: // https://github.com/KSP-KOS/KOS/blob/develop/src/kOS/Suffixed/GeoCoordinates.cs#L136 // Solving the problem to get a perfectly accurate number requires doing physics ray-casting. // For the purposes of *this* mod, the ballpark figure from PQS is good enough, so we don't // bother with the extra computation to get an exact number. // Thanks to DMagic for pointing me at how SCANSat does these calculations: // https://github.com/S-C-A-N/SCANsat/blob/release/SCANsat/SCANcontroller.cs#L2065-L2073 // If the main body has no PQS controller (e.g. the sun), all surface altitudes are implicitly zero. PQS pqs = vessel.mainBody.pqsController; if (pqs == null) { return(0); } // Where will the vessel be at that time? Vector3d position = vessel.orbit.getPositionAtUT(universalTime); // How much will the body have rotated by then? double rotationDegrees = 0; if (vessel.mainBody.rotates) { double numRotations = (universalTime - currentUniversalTime) / vessel.mainBody.rotationPeriod; rotationDegrees = 360.0 * numRotations; } // What will the latitude and longitude be at that time, in radians? double latitude = vessel.mainBody.GetLatitude(position); double longitude = vessel.mainBody.GetLongitude(position) - rotationDegrees; // Convert that to a unit radial vector. Vector3d radialVector = new Vector3d(1, 0, 0); radialVector = QuaternionD.AngleAxis(latitude, Vector3d.forward) * radialVector; radialVector = QuaternionD.AngleAxis(longitude, Vector3d.down) * radialVector; // Now we can get the surface height. double surfaceHeight = pqs.GetSurfaceHeight(radialVector) - pqs.radius; // If we're over water, then this altitude will be the height of the ocean floor, // which we're not interested in. Report that as zero. return((vessel.mainBody.ocean && (surfaceHeight < 0)) ? 0 : surfaceHeight); }
// Inspired by Hyperedit landing functions // https://github.com/Ezriilc/HyperEdit private Vector3d GetVesselPosition() { PQS pqs = vessel.mainBody.pqsController; if (null == pqs) { return(ZERO); } double alt = pqs.GetSurfaceHeight(vessel.mainBody.GetRelSurfaceNVector(0, 0)) - vessel.mainBody.Radius; alt = Math.Max(alt, 0); // Underwater! return(vessel.mainBody.GetRelSurfacePosition(0, 0, alt)); }
/// <summary> /// Update the heat /// </summary> public void Update() { if (!FlightGlobals.ready) { return; } // Get all vessels List <Vessel> vessels = FlightGlobals.Vessels.FindAll(v => v.mainBody == _body); // Loop through them foreach (Vessel vessel in vessels) { Vector3 position = vessel.transform.position; Double distanceToPlanet = Math.Abs(Vector3d.Distance(position, _body.transform.position)) - _ocean.GetSurfaceHeight(_ocean.GetRelativePosition(position)); Double heatingRate = heatCurve.Evaluate((Single)distanceToPlanet); foreach (Part part in vessel.Parts) { part.temperature += heatingRate * Time.deltaTime; } } }
public static void MatchVerts(Mesh mesh, PQS pqs, double oceanHeight, float scaleFactor) { if (pqs == null) { Utils.LogError("pqs is null"); return; } pqs.isBuildingMaps = true; Vector3[] vertices = new Vector3[mesh.vertexCount]; for (int i = 0; i < mesh.vertexCount; i++) { Vector3 v = mesh.vertices[i]; double height = pqs.GetSurfaceHeight(v); if (height < oceanHeight) { height = oceanHeight; } vertices[i] = v.normalized * (float)(1000.0 * height / pqs.radius * scaleFactor); } pqs.isBuildingMaps = false; mesh.vertices = vertices; }
private void UpdateMove() { if (!MovingVessel) { EndMove(); return; } MovingVessel.IgnoreGForces(240); // Lerp is animating move if (!_hoverChanged) { MoveHeight = Mathf.Lerp(MoveHeight, _vBounds.BottomLength + HoverHeight, 10 * Time.fixedDeltaTime); } else { double alt = MovingVessel.radarAltitude; // sINCE Lerp is animating move from 0 to hoverheight, we do not want this going below current altitude if (MoveHeight < alt) { MoveHeight = Convert.ToSingle(alt); } MoveHeight = MovingVessel.Splashed ? Mathf.Lerp(MoveHeight, _vBounds.BottomLength + _hoverAdjust, 10 * Time.fixedDeltaTime) : Mathf.Lerp(MoveHeight, _vBounds.BottomLength + (MoveHeight + _hoverAdjust < 0 ? -MoveHeight : _hoverAdjust), 10 * Time.fixedDeltaTime); } MovingVessel.ActionGroups.SetGroup(KSPActionGroup.RCS, false); _up = (MovingVessel.transform.position - FlightGlobals.currentMainBody.transform.position).normalized; Vector3 forward; if (MapView.MapIsEnabled) { forward = North(); } else { forward = Vector3.ProjectOnPlane(MovingVessel.CoM - FlightCamera.fetch.mainCamera.transform.position, _up).normalized; if (Vector3.Dot(-_up, FlightCamera.fetch.mainCamera.transform.up) > 0) { forward = Vector3.ProjectOnPlane(FlightCamera.fetch.mainCamera.transform.up, _up).normalized; } } Vector3 right = Vector3.Cross(_up, forward); Vector3 offsetDirection = Vector3.zero; bool inputting = false; //Altitude Adjustment if (GameSettings.THROTTLE_CUTOFF.GetKey()) { _hoverAdjust = 0f; _hoverChanged = false; } if (GameSettings.THROTTLE_UP.GetKey()) { _hoverAdjust += MoveSpeed * Time.fixedDeltaTime; inputting = true; _hoverChanged = true; } if (GameSettings.THROTTLE_DOWN.GetKey()) { _hoverAdjust += -(MoveSpeed * Time.fixedDeltaTime); inputting = true; _hoverChanged = true; } if (GameSettings.PITCH_DOWN.GetKey()) { offsetDirection += (forward * MoveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.PITCH_UP.GetKey()) { offsetDirection += (-forward * MoveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.YAW_RIGHT.GetKey()) { offsetDirection += (right * MoveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.YAW_LEFT.GetKey()) { offsetDirection += (-right * MoveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.TRANSLATE_RIGHT.GetKey()) { _startRotation = Quaternion.AngleAxis(-RotationSpeed, MovingVessel.ReferenceTransform.forward) * _startRotation; _hasRotated = true; } else if (GameSettings.TRANSLATE_LEFT.GetKey()) { _startRotation = Quaternion.AngleAxis(RotationSpeed, MovingVessel.ReferenceTransform.forward) * _startRotation; _hasRotated = true; } if (GameSettings.TRANSLATE_DOWN.GetKey()) { _startRotation = Quaternion.AngleAxis(RotationSpeed, MovingVessel.ReferenceTransform.right) * _startRotation; _hasRotated = true; } else if (GameSettings.TRANSLATE_UP.GetKey()) { _startRotation = Quaternion.AngleAxis(-RotationSpeed, MovingVessel.ReferenceTransform.right) * _startRotation; _hasRotated = true; } if (GameSettings.ROLL_LEFT.GetKey()) { _startRotation = Quaternion.AngleAxis(RotationSpeed, MovingVessel.ReferenceTransform.up) * _startRotation; _hasRotated = true; } else if (GameSettings.ROLL_RIGHT.GetKey()) { _startRotation = Quaternion.AngleAxis(-RotationSpeed, MovingVessel.ReferenceTransform.up) * _startRotation; _hasRotated = true; } //auto level plane if (GameSettings.TRANSLATE_FWD.GetKey()) { Quaternion targetRot = Quaternion.LookRotation(-_up, forward); _startRotation = Quaternion.RotateTowards(_startRotation, targetRot, RotationSpeed * 2); _hasRotated = true; } else if (GameSettings.TRANSLATE_BACK.GetKey())//auto level rocket { Quaternion targetRot = Quaternion.LookRotation(forward, _up); _startRotation = Quaternion.RotateTowards(_startRotation, targetRot, RotationSpeed * 2); _hasRotated = true; } if (inputting) { _currMoveSpeed = Mathf.Clamp(Mathf.MoveTowards(_currMoveSpeed, MoveSpeed, MoveAccel * Time.fixedDeltaTime), 0, MoveSpeed); } else { _currMoveSpeed = 0; } Vector3 offset = offsetDirection.normalized * _currMoveSpeed; _currMoveVelocity = offset / Time.fixedDeltaTime; Vector3 vSrfPt = MovingVessel.CoM - (MoveHeight * _up); bool srfBelowWater = false; RaycastHit ringHit = default; bool surfaceDetected = false; var rayCastHits = CapsuleCast(); Array.Sort(rayCastHits, (r1, r2) => r1.distance.CompareTo(r2.distance)); foreach (var hit in rayCastHits) { var partHit = hit.collider.gameObject.GetComponentInParent <Part>(); if (partHit == null) { ringHit = hit; surfaceDetected = true; break; } if (partHit?.vessel == MovingVessel) { continue; } ringHit = hit; surfaceDetected = true; break; } Vector3 finalOffset = Vector3.zero; if (surfaceDetected) { if (FlightGlobals.getAltitudeAtPos(ringHit.point) < 0) { srfBelowWater = true; } Vector3 rOffset = Vector3.Project(ringHit.point - vSrfPt, _up); Vector3 mOffset = (vSrfPt + offset) - MovingVessel.CoM; finalOffset = rOffset + mOffset + (MoveHeight * _up); MovingVessel.Translate(finalOffset); } PQS bodyPQS = MovingVessel.mainBody.pqsController; Vector3d geoCoords = WorldPositionToGeoCoords(MovingVessel.GetWorldPos3D() + (_currMoveVelocity * Time.fixedDeltaTime), MovingVessel.mainBody); double lat = geoCoords.x; double lng = geoCoords.y; Vector3d bodyUpVector = new Vector3d(1, 0, 0); bodyUpVector = QuaternionD.AngleAxis(lat, Vector3d.forward /*around Z axis*/) * bodyUpVector; bodyUpVector = QuaternionD.AngleAxis(lng, Vector3d.down /*around -Y axis*/) * bodyUpVector; double srfHeight = bodyPQS.GetSurfaceHeight(bodyUpVector); #if false { double alt = srfHeight - bodyPQS.radius; double rAlt = movingVessel.radarAltitude; double tAlt = TrueAlt(movingVessel); double pAlt = movingVessel.pqsAltitude; double teralt = movingVessel.mainBody.TerrainAltitude(movingVessel.mainBody.GetLatitude(geoCoords), movingVessel.mainBody.GetLongitude(geoCoords)); Log.dbg("Surface height: {0}", movingVessel.mainBody.pqsController.GetSurfaceHeight(up)); } #endif if (!surfaceDetected || srfBelowWater) { Vector3 terrainPos = MovingVessel.mainBody.position + (float)srfHeight * _up; Vector3 waterSrfPoint = FlightGlobals.currentMainBody.position + ((float)FlightGlobals.currentMainBody.Radius * _up); if (!surfaceDetected) { MovingVessel.SetPosition(terrainPos + (MoveHeight * _up) + offset); } else { MovingVessel.SetPosition(waterSrfPoint + (MoveHeight * _up) + offset); } //update vessel situation to splashed down: MovingVessel.UpdateLandedSplashed(); } //fix surface rotation Quaternion srfRotFix = Quaternion.FromToRotation(_startingUp, _up); _currRotation = srfRotFix * _startRotation; MovingVessel.SetRotation(_currRotation); if (Vector3.Angle(_startingUp, _up) > 5) { _startRotation = _currRotation; _startingUp = _up; } MovingVessel.SetWorldVelocity(Vector3d.zero); MovingVessel.angularVelocity = Vector3.zero; MovingVessel.angularMomentum = Vector3.zero; }
//Scaled Space Updater private void RegenerateModel(PQS bodyPQS, MeshFilter meshfilter_input) { var originalVert = meshfilter_input.mesh.vertices[0]; var originalHeight = (float)bodyPQS.GetSurfaceHeight(originalVert); var scale = originalHeight / originalVert.magnitude; bodyPQS.isBuildingMaps = true; var newVerts = new Vector3[meshfilter_input.mesh.vertices.Count()]; for (int i = 0; i < meshfilter_input.mesh.vertices.Count(); i++) { var vertex = meshfilter_input.mesh.vertices[i]; var rootrad = (float)Math.Sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z); var radius = (float)bodyPQS.GetSurfaceHeight(vertex)/scale; //radius = 1000; newVerts[i] = vertex * (radius / rootrad); } bodyPQS.isBuildingMaps = false; meshfilter_input.mesh.vertices = newVerts; meshfilter_input.mesh.RecalculateNormals(); Utils.RecalculateTangents(meshfilter_input.mesh); }
public static void GenerateScaledSpace(CelestialBody body, Mesh meshinput) { PQS bodyPQS = body.pqsController; Single joolScaledRad = 1000f; Single joolRad = 6000000f; Single scale = (float)bodyPQS.radius / joolScaledRad; Vector3[] vertices = new Vector3[meshinput.vertices.Count()]; // One could use pqs.radiusMin and pqs.radiusMax to determine minimum and maximum height. // But to be safe, the height limit values will be determined manually. Single radiusMin = 0; Single radiusMax = 0; bodyPQS.isBuildingMaps = true; for (Int32 i = 0; i < meshinput.vertices.Count(); i++) { Vector3 vertex = meshinput.vertices[i]; Single rootrad = (float)Math.Sqrt(vertex.x * vertex.x + vertex.y * vertex.y + vertex.z * vertex.z); Single localRadius = (float)bodyPQS.GetSurfaceHeight(vertex) / scale; vertices[i] = vertex * (localRadius / rootrad); if (i == 0) { radiusMin = radiusMax = localRadius; } else { if (radiusMin > localRadius) { radiusMin = localRadius; } if (radiusMax < localRadius) { radiusMax = localRadius; } } } bodyPQS.isBuildingMaps = false; // Adjust the mesh so the maximum radius has 1000 unit in scaled space. // (so the planets will fit in the science archive list) Single r = radiusMax / 1000; for (Int32 i = 0; i < vertices.Count(); i++) { vertices[i] /= r; } // Use the lowest radius as collision radius. Single radius = radiusMin / r; // Calculate the local scale. Vector3 localScale = Vector3.one * ((float)bodyPQS.radius / joolRad) * r; // Apply the mesh to ScaledSpace MeshFilter meshfilter = body.scaledBody.GetComponent <MeshFilter>(); SphereCollider collider = body.scaledBody.GetComponent <SphereCollider>(); meshfilter.sharedMesh.vertices = vertices; meshfilter.sharedMesh.RecalculateNormals(); Utility.RecalculateTangents(meshfilter.sharedMesh); collider.radius = radius; body.scaledBody.transform.localScale = localScale; // Serialize Directory.CreateDirectory(KSPUtil.ApplicationRootPath + Body.ScaledSpaceCacheDirectory); Utility.SerializeMesh(meshfilter.sharedMesh, KSPUtil.ApplicationRootPath + Body.ScaledSpaceCacheDirectory + "/" + body.name + ".bin"); }
private void UpdateMove() { if (!MovingVessel) { // EndMove(); return; } MovingVessel.IgnoreGForces(240); // Lerp is animating move if (!_hoverChanged) { MoveHeight = Mathf.Lerp(MoveHeight, _vBounds.BottomLength + HoverHeight, 10 * Time.fixedDeltaTime); } else { double alt = MovingVessel.radarAltitude; // sINCE Lerp is animating move from 0 to hoverheight, we do not want this going below current altitude if (MoveHeight < alt) { MoveHeight = Convert.ToSingle(alt); } MoveHeight = MovingVessel.Splashed ? Mathf.Lerp(MoveHeight, _vBounds.BottomLength + _hoverAdjust, 10 * Time.fixedDeltaTime) : Mathf.Lerp(MoveHeight, _vBounds.BottomLength + (MoveHeight + _hoverAdjust < 0 ? -MoveHeight : _hoverAdjust), 10 * Time.fixedDeltaTime); } MovingVessel.ActionGroups.SetGroup(KSPActionGroup.RCS, false); _up = (MovingVessel.transform.position - FlightGlobals.currentMainBody.transform.position).normalized; Vector3 forward; if (MapView.MapIsEnabled) { forward = North(); } else { forward = Vector3.ProjectOnPlane(MovingVessel.CoM - FlightCamera.fetch.mainCamera.transform.position, _up).normalized; if (Vector3.Dot(-_up, FlightCamera.fetch.mainCamera.transform.up) > 0) { forward = Vector3.ProjectOnPlane(FlightCamera.fetch.mainCamera.transform.up, _up).normalized; } } Vector3 right = Vector3.Cross(_up, forward); Vector3 offsetDirection = Vector3.zero; bool inputting = false; //auto level plane if (GameSettings.TRANSLATE_FWD.GetKey()) { Quaternion targetRot = Quaternion.LookRotation(-_up, forward); _startRotation = Quaternion.RotateTowards(_startRotation, targetRot, RotationSpeed * 2); _hasRotated = true; } else if (GameSettings.TRANSLATE_BACK.GetKey())//auto level rocket { Quaternion targetRot = Quaternion.LookRotation(forward, _up); _startRotation = Quaternion.RotateTowards(_startRotation, targetRot, RotationSpeed * 2); _hasRotated = true; } if (inputting) { _currMoveSpeed = Mathf.Clamp(Mathf.MoveTowards(_currMoveSpeed, MoveSpeed, MoveAccel * Time.fixedDeltaTime), 0, MoveSpeed); } else { _currMoveSpeed = 0; } Vector3 offset = offsetDirection.normalized * _currMoveSpeed; _currMoveVelocity = offset / Time.fixedDeltaTime; Vector3 vSrfPt = MovingVessel.CoM - (MoveHeight * _up); bool srfBelowWater = false; RaycastHit ringHit; bool surfaceDetected = CapsuleCast(out ringHit); Vector3 finalOffset = Vector3.zero; if (surfaceDetected) { if (FlightGlobals.getAltitudeAtPos(ringHit.point) < 0) { srfBelowWater = true; } Vector3 rOffset = Vector3.Project(ringHit.point - vSrfPt, _up); Vector3 mOffset = (vSrfPt + offset) - MovingVessel.CoM; finalOffset = rOffset + mOffset + (MoveHeight * _up); MovingVessel.Translate(finalOffset); } PQS bodyPQS = MovingVessel.mainBody.pqsController; Vector3d geoCoords = WorldPositionToGeoCoords(MovingVessel.GetWorldPos3D() + (_currMoveVelocity * Time.fixedDeltaTime), MovingVessel.mainBody); double lat = geoCoords.x; double lng = geoCoords.y; Vector3d bodyUpVector = new Vector3d(1, 0, 0); bodyUpVector = QuaternionD.AngleAxis(lat, Vector3d.forward /*around Z axis*/) * bodyUpVector; bodyUpVector = QuaternionD.AngleAxis(lng, Vector3d.down /*around -Y axis*/) * bodyUpVector; double srfHeight = bodyPQS.GetSurfaceHeight(bodyUpVector); if (!surfaceDetected || srfBelowWater) { Vector3 terrainPos = MovingVessel.mainBody.position + (float)srfHeight * _up; Vector3 waterSrfPoint = FlightGlobals.currentMainBody.position + ((float)FlightGlobals.currentMainBody.Radius * _up); if (!surfaceDetected) { MovingVessel.SetPosition(terrainPos + (MoveHeight * _up) + offset); } else { MovingVessel.SetPosition(waterSrfPoint + (MoveHeight * _up) + offset); } //update vessel situation to splashed down: //MovingVessel.UpdateLandedSplashed(); } //fix surface rotation Quaternion srfRotFix = Quaternion.FromToRotation(_startingUp, _up); _currRotation = srfRotFix * _startRotation; MovingVessel.SetRotation(_currRotation); if (Vector3.Angle(_startingUp, _up) > 5) { _startRotation = _currRotation; _startingUp = _up; } MovingVessel.SetWorldVelocity(Vector3d.zero); MovingVessel.angularVelocity = Vector3.zero; MovingVessel.angularMomentum = Vector3.zero; }
void FixAltitude() { CelestialBody body = FlightGlobals.currentMainBody; if (body == null) { return; } PQS pqs = body.pqsController; if (pqs == null) { return; } PQSCity city = GetComponent <PQSCity>(); if (city == null) { return; } // Location Vector3 planet = body.transform.position; Vector3 building = city.transform.position; // From body to city Vector3 location = (building - planet).normalized; // Sigma Dimensions Settings double resize = body.Has("resize") ? body.Get <double>("resize") : 1; double landscape = body.Has("landscape") ? body.Get <double>("landscape") : 1; double resizeBuildings = body.Has("resizeBuildings") ? body.Get <double>("resizeBuildings") : 1; // Max distance double maxDistance = Math.Abs(2 * pqs.mapMaxHeight); maxDistance *= resize * landscape > 1 ? resize * landscape : 1; maxDistance += body.Radius; RaycastHit[] hits = Physics.RaycastAll(planet + location * (float)maxDistance, -location, (float)maxDistance, LayerMask.GetMask("Local Scenery")); for (int i = 0; i < hits?.Length; i++) { if (hits[i].collider?.GetComponent <PQ>()) { // Update only once TimingManager.UpdateRemove(TimingManager.TimingStage.Normal, FixAltitude); Debug.Log("PQSCityFixer", "> Planet: " + body.transform.name); Debug.Log("PQSCityFixer", " > PQSCity: " + city); // PQSCity parameters double groundLevel = (hits[i].point - planet).magnitude - body.Radius; double error = pqs.GetSurfaceHeight(city.repositionRadial) - body.Radius - groundLevel; double oceanDepth = body.ocean && groundLevel < 0 ? -groundLevel : 0d; groundLevel = body.ocean && groundLevel < 0 ? 0d : groundLevel; Debug.Log("PQSCityFixer", " > Ground Level at Mod = " + groundLevel); Debug.Log("PQSCityFixer", " > Ocean Depth at Mod = " + groundLevel); Debug.Log("PQSCityFixer", " > Ground Level Error at Mod = " + groundLevel); // Fix Altitude if (city.repositionToSphere && !city.repositionToSphereSurface) { // Offset = Distance from the radius of the planet Debug.Log("PQSCityFixer", " > PQSCity Original Radius Offset = " + city.repositionRadiusOffset); double builtInOffset = city.repositionRadiusOffset - groundLevel / (resize * landscape); Debug.Log("PQSCityFixer", " > Builtuin Offset = " + builtInOffset); city.repositionRadiusOffset = groundLevel + error / (resize * landscape) - (groundLevel + error - city.repositionRadiusOffset) / resizeBuildings; Debug.Log("PQSCityFixer", " > PQSCity Fixed Radius Offset = " + city.repositionRadiusOffset); } else { // Offset = Distance from the surface of the planet if (!city.repositionToSphereSurface) { city.repositionToSphereSurface = true; city.repositionRadiusOffset = 0; } if (!city.repositionToSphereSurfaceAddHeight) { city.repositionToSphereSurfaceAddHeight = true; city.repositionRadiusOffset = 0; } Debug.Log("PQSCityFixer", " > PQSCity Original Surface Offset = " + city.repositionRadiusOffset); city.repositionRadiusOffset = oceanDepth + error / (resize * landscape) - (oceanDepth + error - city.repositionRadiusOffset) / resizeBuildings; Debug.Log("PQSCityFixer", " > PQSCity Fixed Surface Offset = " + city.repositionRadiusOffset); } city.Orientate(); DestroyImmediate(this); } } }
private void UpdateMove() { if (!_moving) return; MovingVessel.IgnoreGForces(240); // Lerp is animating move if (!_hoverChanged) { //MoveHeight = Mathf.Lerp(MoveHeight, Convert.ToSingle(MovingVessel.radarAltitude), 10 * Time.fixedDeltaTime);// + HoverHeight, 10 * Time.fixedDeltaTime); } else { _alt = MoveHeight; if (MoveHeight < _alt) MoveHeight = (float)_alt; if (MoveHeight > _alt) MoveHeight = (float)_alt; MoveHeight = Mathf.Lerp(MoveHeight, (float)_alt + _hoverAdjust, 10 * Time.fixedDeltaTime); } MovingVessel.ActionGroups.SetGroup(KSPActionGroup.RCS, false); _hoverAdjust = 0; if (_externalControl) { if (MovingVessel.radarAltitude >= MovingVessel.altitude) { MoveHeight = (float)MovingVessel.radarAltitude - (float)MovingVessel.altitude; } if (MoveHeight <= 10) { MoveHeight = 10; } if (_distance >= 15 && _moving) { if (OrXUtilities.instance.GetDistance(MovingVessel.longitude, MovingVessel.latitude, targetPosition.y, targetPosition.x, (MovingVessel.altitude + targetPosition.z) / 2) >= _distance * 0.5) { MoveHeight += 0.1f; } else { MoveHeight -= 0.1f; } } } if (MoveHeight <= 10) { MoveSpeed = (float)MovingVessel.radarAltitude / 3; } else { if (MoveHeight >= 50) { if (MoveHeight >= 100) { _lineRender.material.SetColor("_EmissiveColor", XKCDColors.BluePurple); MoveSpeed = 100 + ((float)MovingVessel.radarAltitude / 10); } else { _lineRender.material.SetColor("_EmissiveColor", XKCDColors.AquaGreen); MoveSpeed = (float)MovingVessel.radarAltitude; } } else { MoveSpeed = (float)MovingVessel.radarAltitude / 2; } } UpVect = (MovingVessel.transform.position - FlightGlobals.currentMainBody.transform.position).normalized; right = Vector3.Cross(UpVect, forward); offsetDirection = Vector3.zero; bool inputting = false; _moveSpeed = 0; //Altitude Adjustment _moveSpeed = MoveSpeed; if (!_externalControl) { if (MapView.MapIsEnabled) { forward = North(); } else { forward = Vector3.ProjectOnPlane(MovingVessel.CoM - FlightCamera.fetch.mainCamera.transform.position, UpVect).normalized; if (Vector3.Dot(-UpVect, FlightCamera.fetch.mainCamera.transform.up) > 0) { forward = Vector3.ProjectOnPlane(FlightCamera.fetch.mainCamera.transform.up, UpVect).normalized; } } if (placingGate) { if (MoveHeight >= 20) { MoveHeight = 20; } } if (GameSettings.PITCH_DOWN.GetKey()) { offsetDirection += (forward * _moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.PITCH_UP.GetKey()) { offsetDirection += (-forward * _moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.YAW_RIGHT.GetKey()) { offsetDirection += (right * _moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.YAW_LEFT.GetKey()) { offsetDirection += (-right * _moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.THROTTLE_UP.GetKey()) { if (MovingVessel.radarAltitude <= 3000) { if (MovingVessel.altitude - MovingVessel.radarAltitude <= 0) { MoveHeight += (float)MovingVessel.altitude / 100; } else { MoveHeight += (float)MovingVessel.radarAltitude / 100; } } } if (GameSettings.THROTTLE_DOWN.GetKey()) { if (MovingVessel.radarAltitude >= 1) { if (MovingVessel.altitude - MovingVessel.radarAltitude <= 0) { MoveHeight -= (float)MovingVessel.altitude / 100; } else { MoveHeight -= (float)MovingVessel.radarAltitude / 100; } } } if (GameSettings.TRANSLATE_RIGHT.GetKey()) { _startRotation = Quaternion.AngleAxis(-RotationSpeed, MovingVessel.ReferenceTransform.forward) * _startRotation; _hasRotated = true; } else if (GameSettings.TRANSLATE_LEFT.GetKey()) { _startRotation = Quaternion.AngleAxis(RotationSpeed, MovingVessel.ReferenceTransform.forward) * _startRotation; _hasRotated = true; } if (GameSettings.TRANSLATE_DOWN.GetKey()) { _startRotation = Quaternion.AngleAxis(RotationSpeed, MovingVessel.ReferenceTransform.right) * _startRotation; _hasRotated = true; } else if (GameSettings.TRANSLATE_UP.GetKey()) { _startRotation = Quaternion.AngleAxis(-RotationSpeed, MovingVessel.ReferenceTransform.right) * _startRotation; _hasRotated = true; } if (GameSettings.ROLL_LEFT.GetKey()) { _startRotation = Quaternion.AngleAxis(RotationSpeed, MovingVessel.ReferenceTransform.up) * _startRotation; _hasRotated = true; } else if (GameSettings.ROLL_RIGHT.GetKey()) { _startRotation = Quaternion.AngleAxis(-RotationSpeed, MovingVessel.ReferenceTransform.up) * _startRotation; _hasRotated = true; } } else { forward = Vector3.ProjectOnPlane(MovingVessel.CoM - OrXHoloKron.instance.targetLoc, UpVect).normalized; offsetDirection += (-forward * _moveSpeed * Time.fixedDeltaTime); } if (spawningLocal) { } if (inputting || _externalControl) { _currMoveSpeed = Mathf.Clamp(Mathf.MoveTowards(_currMoveSpeed, _moveSpeed, MoveAccel * Time.fixedDeltaTime), 0, MoveSpeed); } else { _currMoveSpeed = 0; } Vector3 offset = offsetDirection.normalized * _currMoveSpeed; _currMoveVelocity = offset / Time.fixedDeltaTime; Vector3 vSrfPt = MovingVessel.CoM - (MoveHeight * UpVect); RaycastHit ringHit; bool surfaceDetected = CapsuleCast(out ringHit); Vector3 finalOffset = Vector3.zero; Vector3 rOffset = Vector3.Project(ringHit.point - vSrfPt, UpVect); Vector3 mOffset = (vSrfPt + offset) - MovingVessel.CoM; finalOffset = rOffset + mOffset + (MoveHeight * UpVect); if (!_moving) return; MovingVessel.Translate(finalOffset); PQS bodyPQS = MovingVessel.mainBody.pqsController; Vector3d geoCoords = OrXUtilities.instance.WorldPositionToGeoCoords(MovingVessel.GetWorldPos3D() + (_currMoveVelocity * Time.fixedDeltaTime), MovingVessel.mainBody); double lat = geoCoords.x; double lng = geoCoords.y; Vector3d bodyUpVector = new Vector3d(1, 0, 0); bodyUpVector = QuaternionD.AngleAxis(lat, Vector3d.forward/*around Z axis*/) * bodyUpVector; bodyUpVector = QuaternionD.AngleAxis(lng, Vector3d.down/*around -Y axis*/) * bodyUpVector; double srfHeight = bodyPQS.GetSurfaceHeight(bodyUpVector); if (!surfaceDetected) { Vector3 terrainPos = MovingVessel.mainBody.position + (float)srfHeight * UpVect; if (!surfaceDetected) { MovingVessel.SetPosition(terrainPos + (MoveHeight * UpVect) + offset); } } //fix surface rotation Quaternion srfRotFix = Quaternion.FromToRotation(_startingUp, UpVect); _currRotation = srfRotFix * _startRotation; MovingVessel.SetRotation(_currRotation); if (Vector3.Angle(_startingUp, UpVect) > 5) { _startRotation = _currRotation; _startingUp = UpVect; } MovingVessel.SetWorldVelocity(Vector3d.zero); if (_externalControl) { double _dist = OrXUtilities.instance.GetDistance(MovingVessel.longitude, MovingVessel.latitude, targetPosition.y, targetPosition.x, (MovingVessel.altitude + targetPosition.z) / 2); if (OrXHoloKron.instance.triggerVessel != MovingVessel) { if (_dist <= MoveHeight) { KillMove(true, false); } } else { if (_dist <= MoveHeight) { if (OrXHoloKron.instance._placingChallenger) { _moving = false; _externalControl = false; MovingVessel.SetWorldVelocity(Vector3.zero); MovingVessel.IgnoreGForces(240); spawningLocal = false; OrXLog.instance.DebugLog("[OrX Vessel Move] ===== KILLING MOVE ====="); _lineRender.enabled = false; IsMovingVessel = false; MoveHeight = 0; _hoverAdjust = 0f; MovingVessel = new Vessel(); OrXHoloKron.instance._holdVesselPos = false; _rb = MovingVessel.GetComponent<Rigidbody>(); _rb.isKinematic = true; _rb = null; OrXLog.instance.ResetFocusKeys(); OrXHoloKron.instance.OpenScoreboardMenu(); } else { KillMove(true, false); } } } } }
public static void MatchVerts(Mesh mesh, PQS pqs, double oceanHeight, float scaleFactor) { ProfileTimer.Push("MatchVerts"); if (pqs == null) { MonoBehaviour.print("ERROR matching verts: pqs is null!"); return; } char sep = System.IO.Path.DirectorySeparatorChar; pqs.isBuildingMaps = true; Vector3[] vertices = new Vector3[mesh.vertexCount]; for (int i = 0; i < mesh.vertexCount; i++) { Vector3 v = mesh.vertices[i]; double height = pqs.GetSurfaceHeight(v); if (height < oceanHeight) height = oceanHeight; vertices[i] = v.normalized * (float)(1000.0 * height / pqs.radius * scaleFactor); } pqs.isBuildingMaps = false; mesh.vertices = vertices; ProfileTimer.Pop("MatchVerts"); }
void Update() { CelestialBody body = FlightGlobals.currentMainBody; if (body == null) { return; } PQS pqs = body.pqsController; if (pqs == null) { return; } PQSCity2 city = GetComponent <PQSCity2>(); if (city == null) { return; } // Location Vector3 planet = body.transform.position; Vector3 building = city.transform.position; // From body to city Vector3 location = (building - planet).normalized; // Sigma Dimensions Settings double resize = body.Has("resize") ? body.Get <double>("resize") : 1; double landscape = body.Has("landscape") ? body.Get <double>("landscape") : 1; double resizeBuildings = body.Has("resizeBuildings") ? body.Get <double>("resizeBuildings") : 1; // Max distance double maxDistance = Math.Abs(2 * pqs.mapMaxHeight); maxDistance *= resize * landscape > 1 ? resize * landscape : 1; maxDistance += body.Radius; RaycastHit[] hits = Physics.RaycastAll(planet + location * (float)maxDistance, -location, (float)maxDistance, LayerMask.GetMask("Local Scenery")); for (int i = 0; i < hits?.Length; i++) { if (hits[i].collider?.GetComponent <PQ>()) { Debug.Log("PQSCity2Fixer", "> Planet: " + body.transform.name); Debug.Log("PQSCity2Fixer", " > PQSCity2: " + city); // PQSCity2 parameters double groundLevel = (hits[i].point - planet).magnitude - body.Radius; Debug.Log("PQSCity2Fixer", " > Ground Level at Mod (RAYCAST) = " + groundLevel); double error = pqs.GetSurfaceHeight(city.PlanetRelativePosition) - body.Radius - groundLevel; Debug.Log("PQSCity2Fixer", " > Ground Level Error at Mod = " + error); double oceanDepth = body.ocean && groundLevel < 0 ? -groundLevel : 0d; Debug.Log("PQSCity2Fixer", " > Ocean Depth at Mod = " + oceanDepth); groundLevel = body.ocean && groundLevel < 0 ? 0d : groundLevel; Debug.Log("PQSCity2Fixer", " > Ground Level at Mod (NEW) = " + groundLevel); // Because, SQUAD city.PositioningPoint.localPosition /= (float)(body.Radius + city.alt); // Fix Altitude if (!city.snapToSurface) { // Alt = Distance from the radius of the planet Debug.Log("PQSCity2Fixer", " > PQSCity2 Original Radius Offset = " + city.alt); double builtInOffset = city.alt - groundLevel / (resize * landscape); Debug.Log("PQSCity2Fixer", " > Builtuin Offset = " + builtInOffset); city.alt = groundLevel + error / (resize * landscape) - (groundLevel + error - city.alt) / resizeBuildings; Debug.Log("PQSCity2Fixer", " > PQSCity2 Fixed Radius Offset = " + city.alt); } else { // Offset = Distance from the surface of the planet Debug.Log("PQSCity2Fixer", " > PQSCity2 Original Surface Offset = " + city.snapHeightOffset); double newOffset = oceanDepth + error / (resize * landscape) - (oceanDepth + error - city.snapHeightOffset) / resizeBuildings; city.alt += newOffset - city.snapHeightOffset; city.snapHeightOffset = newOffset; Debug.Log("PQSCity2Fixer", " > PQSCity2 New Surface Offset = " + city.snapHeightOffset); } // Because, SQUAD city.PositioningPoint.localPosition *= (float)(body.Radius + city.alt); // Apply Changes and Destroy city.Orientate(); DestroyImmediate(this); } } }
void UpdateMove() { if (!movingVessel) { EndMove(); return; } moveHeight = Mathf.Lerp(moveHeight, vBounds.bottomLength + hoverHeight, 10 * Time.fixedDeltaTime); movingVessel.ActionGroups.SetGroup(KSPActionGroup.RCS, false); up = (movingVessel.transform.position - FlightGlobals.currentMainBody.transform.position).normalized; Vector3 forward; if (MapView.MapIsEnabled) { forward = North(); } else { forward = Vector3.ProjectOnPlane(movingVessel.CoM - FlightCamera.fetch.mainCamera.transform.position, up).normalized; if (Vector3.Dot(-up, FlightCamera.fetch.mainCamera.transform.up) > 0) { forward = Vector3.ProjectOnPlane(FlightCamera.fetch.mainCamera.transform.up, up).normalized; } } Vector3 right = Vector3.Cross(up, forward); Vector3 offsetDirection = Vector3.zero; bool inputting = false; if (GameSettings.PITCH_DOWN.GetKey()) { offsetDirection += (forward * moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.PITCH_UP.GetKey()) { offsetDirection += (-forward * moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.YAW_RIGHT.GetKey()) { offsetDirection += (right * moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.YAW_LEFT.GetKey()) { offsetDirection += (-right * moveSpeed * Time.fixedDeltaTime); inputting = true; } if (GameSettings.TRANSLATE_RIGHT.GetKey()) { startRotation = Quaternion.AngleAxis(-rotationSpeed, movingVessel.ReferenceTransform.forward) * startRotation; hasRotated = true; } else if (GameSettings.TRANSLATE_LEFT.GetKey()) { startRotation = Quaternion.AngleAxis(rotationSpeed, movingVessel.ReferenceTransform.forward) * startRotation; hasRotated = true; } if (GameSettings.TRANSLATE_DOWN.GetKey()) { startRotation = Quaternion.AngleAxis(rotationSpeed, movingVessel.ReferenceTransform.right) * startRotation; hasRotated = true; } else if (GameSettings.TRANSLATE_UP.GetKey()) { startRotation = Quaternion.AngleAxis(-rotationSpeed, movingVessel.ReferenceTransform.right) * startRotation; hasRotated = true; } if (GameSettings.ROLL_LEFT.GetKey()) { startRotation = Quaternion.AngleAxis(rotationSpeed, movingVessel.ReferenceTransform.up) * startRotation; hasRotated = true; } else if (GameSettings.ROLL_RIGHT.GetKey()) { startRotation = Quaternion.AngleAxis(-rotationSpeed, movingVessel.ReferenceTransform.up) * startRotation; hasRotated = true; } //auto level plane if (GameSettings.TRANSLATE_FWD.GetKey()) { Quaternion targetRot = Quaternion.LookRotation(-up, forward); startRotation = Quaternion.RotateTowards(startRotation, targetRot, rotationSpeed * 2); hasRotated = true; } else if (GameSettings.TRANSLATE_BACK.GetKey()) //auto level rocket { Quaternion targetRot = Quaternion.LookRotation(forward, up); startRotation = Quaternion.RotateTowards(startRotation, targetRot, rotationSpeed * 2); hasRotated = true; } if (inputting) { currMoveSpeed = Mathf.Clamp(Mathf.MoveTowards(currMoveSpeed, moveSpeed, moveAccel * Time.fixedDeltaTime), 0, moveSpeed); } else { currMoveSpeed = 0; } Vector3 offset = offsetDirection.normalized * currMoveSpeed; currMoveVelocity = offset / Time.fixedDeltaTime; Vector3 vSrfPt = movingVessel.CoM - (moveHeight * up); bool srfBelowWater = false; RaycastHit ringHit; //bool surfaceDetected = RingCast(out ringHit, 8); bool surfaceDetected = CapsuleCast(out ringHit); Vector3 finalOffset = Vector3.zero; if (surfaceDetected) { if (FlightGlobals.getAltitudeAtPos(ringHit.point) < 0) { srfBelowWater = true; } Vector3 rOffset = Vector3.Project(ringHit.point - vSrfPt, up); Vector3 mOffset = (vSrfPt + offset) - movingVessel.CoM; finalOffset = rOffset + mOffset + (moveHeight * up); movingVessel.Translate(finalOffset); } Vector3d geoCoords = WorldPositionToGeoCoords(movingVessel.GetWorldPos3D() + (currMoveVelocity * Time.fixedDeltaTime), movingVessel.mainBody); PQS bodyPQS = movingVessel.mainBody.pqsController; double Lat = geoCoords.x; double Lng = geoCoords.y; var bodyUpVector = new Vector3d(1, 0, 0); bodyUpVector = QuaternionD.AngleAxis(Lat, Vector3d.forward /*around Z axis*/) * bodyUpVector; bodyUpVector = QuaternionD.AngleAxis(Lng, Vector3d.down /*around -Y axis*/) * bodyUpVector; double srfHeight = bodyPQS.GetSurfaceHeight(bodyUpVector); double alt = srfHeight - bodyPQS.radius; if (!surfaceDetected || srfBelowWater) { //Debug.Log ("Surface height: "+movingVessel.mainBody.pqsController.GetSurfaceHeight(up)); Vector3 terrainPos = movingVessel.mainBody.position + (float)srfHeight * up; if (alt > 0) { movingVessel.SetPosition(terrainPos + (moveHeight * up) + offset); //currMoveSpeed/=2; //this would slow you down in order to let terrain load, however, hovering high enough makes it so terrain doesnt matter } else { Vector3 waterSrfPoint = FlightGlobals.currentMainBody.position + ((float)FlightGlobals.currentMainBody.Radius * up); movingVessel.SetPosition(waterSrfPoint + (moveHeight * up) + offset); } } //fix surface rotation Quaternion srfRotFix = Quaternion.FromToRotation(startingUp, up); currRotation = srfRotFix * startRotation; movingVessel.SetRotation(currRotation); if (Vector3.Angle(startingUp, up) > 5) { startRotation = currRotation; startingUp = up; } movingVessel.SetWorldVelocity(Vector3d.zero); movingVessel.angularVelocity = Vector3.zero; movingVessel.angularMomentum = Vector3.zero; }
void Update() { if (time < 0.2) { time += Time.deltaTime; } else { time = 0; CelestialBody body = FlightGlobals.currentMainBody; if (body == null) { return; } PQS pqs = body.pqsController; if (pqs == null) { return; } PQSCity city = GetComponent <PQSCity>(); if (city == null) { return; } // Location Vector3 planet = body.transform.position; Vector3 building = city.transform.position; // From body to city Vector3 location = (building - planet).normalized; // Sigma Dimensions Settings double resize = body.Has("resize") ? body.Get <double>("resize") : 1; double landscape = body.Has("landscape") ? body.Get <double>("landscape") : 1; double resizeBuildings = body.Has("resizeBuildings") ? body.Get <double>("resizeBuildings") : 1; // Max distance double maxDistance = Math.Abs(2 * pqs.mapMaxHeight); maxDistance *= resize * landscape > 1 ? resize * landscape : 1; maxDistance += body.Radius; RaycastHit[] hits = Physics.RaycastAll(planet + location * (float)maxDistance, -location, (float)maxDistance, LayerMask.GetMask("Local Scenery")); for (int i = 0; i < hits?.Length; i++) { if (hits[i].collider?.GetComponent <PQ>()) { Debug.Log("PQSCityFixer", "> Planet: " + body.transform.name); Debug.Log("PQSCityFixer", " > PQSCity: " + city); // PQSCity parameters double oldGroundLevel = pqs.GetSurfaceHeight(city.repositionRadial) - body.Radius; Debug.Log("PQSCityFixer", " > Old Ground Level at Mod (GETSURFACE) = " + oldGroundLevel); double oldOceanOffset = body.ocean && oldGroundLevel < 0 ? oldGroundLevel : 0d; Debug.Log("PQSCityFixer", " > Old Ocean Offset at Mod = " + oldOceanOffset); oldGroundLevel = body.ocean && oldGroundLevel < 0 ? 0d : oldGroundLevel; Debug.Log("PQSCityFixer", " > Old Ground Level at Mod (WITH OCEAN) = " + oldGroundLevel); double groundLevel = (hits[i].point - planet).magnitude - body.Radius; Debug.Log("PQSCityFixer", " > Ground Level at Mod (RAYCAST) = " + groundLevel); double oceanOffset = body.ocean && groundLevel < 0 ? groundLevel : 0d; Debug.Log("PQSCityFixer", " > Ocean Offset at Mod = " + oceanOffset); groundLevel = body.ocean && groundLevel < 0 ? 0d : groundLevel; Debug.Log("PQSCityFixer", " > Ground Level at Mod (WITH OCEAN) = " + groundLevel); // Fix Altitude if (!city.repositionToSphere && !city.repositionToSphereSurface) { // Offset = Distance from the center of the planet // THIS IS NOT POSSIBLE AS OF KSP 1.7.1 Debug.Log("PQSCityFixer", " > PQSCity Current Center Offset = " + city.repositionRadiusOffset); double builtInOffset = (city.repositionRadiusOffset - body.Radius - oldGroundLevel - oceanOffset) / resizeBuildings - (groundLevel + oceanOffset - oldGroundLevel - oldOceanOffset) / (resize * landscape); Debug.Log("PQSCityFixer", " > Builtin Offset = " + builtInOffset); city.repositionRadiusOffset = body.Radius + groundLevel + oceanOffset + builtInOffset * resizeBuildings; Debug.Log("PQSCityFixer", " > PQSCity Fixed Center Offset = " + city.repositionRadiusOffset); } else if (city.repositionToSphere && !city.repositionToSphereSurface) { // Offset = Distance from the radius of the planet Debug.Log("PQSCityFixer", " > PQSCity Current Radius Offset = " + city.repositionRadiusOffset); double builtInOffset = (city.repositionRadiusOffset - oldGroundLevel) / resizeBuildings - (groundLevel - oldGroundLevel) / (resize * landscape); Debug.Log("PQSCityFixer", " > Builtin Offset = " + builtInOffset); city.repositionRadiusOffset = groundLevel + builtInOffset * resizeBuildings; Debug.Log("PQSCityFixer", " > PQSCity Fixed Radius Offset = " + city.repositionRadiusOffset); } else { // Offset = Distance from the surface of the planet Debug.Log("PQSCityFixer", " > PQSCity Current Surface Offset = " + city.repositionRadiusOffset); double builtInOffset = city.repositionRadiusOffset / resizeBuildings - (groundLevel + oceanOffset - oldGroundLevel - oldOceanOffset) / (resize * landscape); Debug.Log("PQSCityFixer", " > Builtin Offset = " + builtInOffset); city.repositionRadiusOffset = builtInOffset * resizeBuildings + groundLevel + oceanOffset - oldGroundLevel - oldOceanOffset; Debug.Log("PQSCityFixer", " > PQSCity Fixed Surface Offset = " + city.repositionRadiusOffset); } // Apply Changes and Destroy city.Orientate(); DestroyImmediate(this); } } } }