public static Vector3 GetAirToAirFireSolution(MissileBase missile, Vessel targetVessel) { if (!targetVessel) { return(missile.transform.position + (missile.GetForwardTransform() * 1000)); } Vector3 targetPosition = targetVessel.transform.position; float leadTime = 0; float targetDistance = Vector3.Distance(targetVessel.transform.position, missile.transform.position); Vector3 simMissileVel = 500 * (targetPosition - missile.transform.position).normalized; MissileLauncher launcher = missile as MissileLauncher; float optSpeed = 400; //TODO: Add parameter if (launcher != null) { optSpeed = launcher.optimumAirspeed; } simMissileVel = optSpeed * (targetPosition - missile.transform.position).normalized; leadTime = targetDistance / (float)(targetVessel.Velocity() - simMissileVel).magnitude; leadTime = Mathf.Clamp(leadTime, 0f, 8f); targetPosition = targetPosition + (targetVessel.Velocity() * leadTime); if (targetVessel && targetDistance < 800) { targetPosition += (Vector3)targetVessel.acceleration * 0.05f * leadTime * leadTime; } return(targetPosition); }
/// <summary> /// Gets the dynamic launch parameters. /// </summary> /// <returns>The dynamic launch parameters.</returns> /// <param name="launcherVelocity">Launcher velocity.</param> /// <param name="targetVelocity">Target velocity.</param> public static MissileLaunchParams GetDynamicLaunchParams(MissileBase missile, Vector3 targetVelocity, Vector3 targetPosition) { Vector3 launcherVelocity = missile.vessel.Velocity(); float launcherSpeed = (float)missile.vessel.srfSpeed; float minLaunchRange = missile.minStaticLaunchRange; float maxLaunchRange = missile.maxStaticLaunchRange; float rangeAddMin = 0; float rangeAddMax = 0; float relSpeed; Vector3 relV = targetVelocity - launcherVelocity; Vector3 vectorToTarget = targetPosition - missile.part.transform.position; Vector3 relVProjected = Vector3.Project(relV, vectorToTarget); relSpeed = -Mathf.Sign(Vector3.Dot(relVProjected, vectorToTarget)) * relVProjected.magnitude; rangeAddMin += relSpeed * 2; rangeAddMax += relSpeed * 8; rangeAddMin += launcherSpeed * 2; rangeAddMax += launcherSpeed * 2; double diffAlt = missile.vessel.altitude - FlightGlobals.getAltitudeAtPos(targetPosition); rangeAddMax += (float)diffAlt; float min = Mathf.Clamp(minLaunchRange + rangeAddMin, 0, BDArmorySettings.MAX_ENGAGEMENT_RANGE); float max = Mathf.Clamp(maxLaunchRange + rangeAddMax, min + 100, BDArmorySettings.MAX_ENGAGEMENT_RANGE); return(new MissileLaunchParams(min, max)); }
IEnumerator Fire() { Vector3 direction = new Vector3( 5f * Mathf.Cos((angle * Mathf.PI) / 180f) + transform.position.x, 5f * Mathf.Sin((angle * Mathf.PI) / 180f) + transform.position.y, 0 ); for (int i = 0; i < 3; i++) { yield return(new WaitForSeconds(0.05f)); GameObject missile = GameObject.Instantiate(basicMissile, shotOriginPoint.transform.position, transform.rotation); Physics.IgnoreCollision(GetComponentInChildren <Collider>(), missile.GetComponentInChildren <Collider>()); foreach (GameObject igo in ignoredObjects) { if (igo != null) { Physics.IgnoreCollision(igo.GetComponentInChildren <Collider>(), missile.GetComponentInChildren <Collider>()); } } Destroy(missile, 7); MissileBase missileBase = missile.GetComponent <MissileBase> (); missileBase.WithForce(direction); missileBase.Fire(); } }
void Awake() { if (!vessel) { vessel = GetComponent <Vessel>(); } if (!vessel) { //Debug.Log ("[BDArmory]: TargetInfo was added to a non-vessel"); Destroy(this); return; } //destroy this if a target info is already attached to the vessel foreach (var otherInfo in vessel.gameObject.GetComponents <TargetInfo>()) { if (otherInfo != this) { Destroy(this); return; } } Team = null; var mf = VesselModuleRegistry.GetMissileFire(vessel, true); if (mf != null) { Team = mf.Team; weaponManager = mf; } else { var ml = VesselModuleRegistry.GetMissileBase(vessel, true); if (ml != null) { isMissile = true; MissileBaseModule = ml; Team = ml.Team; } } vessel.OnJustAboutToBeDestroyed += AboutToBeDestroyed; //add delegate to peace enable event BDArmorySetup.OnPeaceEnabled += OnPeaceEnabled; //lifeRoutine = StartCoroutine(LifetimeRoutine()); // TODO: CHECK BEHAVIOUR AND SIDE EFFECTS! if (!isMissile && Team != null) { GameEvents.onVesselPartCountChanged.Add(VesselModified); //massRoutine = StartCoroutine(MassRoutine()); // TODO: CHECK BEHAVIOUR AND SIDE EFFECTS! } UpdateTargetPartList(); GameEvents.onVesselDestroy.Add(CleanFriendliesEngaging); }
private void MakeDecisionAboutPitch(MissileBase missile, double missileAltitude) { _futureAltitude = CalculateFutureAltitude(_missile.CruisePredictionTime); PitchDecision futureDecision; if (this.GuidanceState != GuidanceState.Terminal && (missileAltitude < 4d || CalculateFutureAltitude(1f) < 4d)) { futureDecision = PitchDecision.EmergencyAscent; } else if (this.GuidanceState != GuidanceState.Terminal && CalculateFutureCollision(_missile.CruisePredictionTime)) { futureDecision = PitchDecision.EmergencyAscent; } else if (_futureAltitude < missile.CruiseAltitude || missileAltitude < missile.CruiseAltitude) { futureDecision = PitchDecision.Ascent; } else if (_futureAltitude > missile.CruiseAltitude || missileAltitude > missile.CruiseAltitude) { futureDecision = PitchDecision.Descent; } else { futureDecision = PitchDecision.Hold; } switch (futureDecision) { case PitchDecision.EmergencyAscent: if (PitchDecision == futureDecision) { _pitchAngle = Mathf.Clamp(_pitchAngle + 1f, 1.5f, 100f); } else { _pitchAngle = 1.5f; } break; case PitchDecision.Ascent: _pitchAngle = Mathf.Clamp(_pitchAngle + 0.0055f, -1.5f, 1.5f); break; case PitchDecision.Descent: _pitchAngle = Mathf.Clamp(_pitchAngle - 0.0025f, -1.5f, 1.5f); break; case PitchDecision.Hold: break; } PitchDecision = futureDecision; }
private double CalculateFreeFallTime(MissileBase missile, int predictionTime = 10) { double vi = CalculateFutureVerticalSpeed(missile, predictionTime) * -1; double a = 9.80665f * missile.BallisticOverShootFactor; double d = missile.vessel.GetFutureAltitude(predictionTime); double time1 = (-vi + Math.Sqrt(Math.Pow(vi, 2) - 4 * (0.5f * a) * (-d))) / a; double time2 = (-vi - Math.Sqrt(Math.Pow(vi, 2) - 4 * (0.5f * a) * (-d))) / a; return(Math.Max(time1, time2)); }
public bool ProcessMessage(MissileFireEventArgs message) { if (message == null) { return(false); } Vessel vessel = FlightGlobals.VesselsLoaded.FirstOrDefault(v => v.id == message.VesselId); if (vessel == null || vessel.packed) { if (!LunaMultiplayerSystem.missileMessagePending.Contains(message)) { LunaMultiplayerSystem.missileMessagePending.Enqueue(message); } return(false); } else { if (!vessel.loaded) { vessel.Load(); } } MissileBase missile = vessel.FindPartModuleImplementing <MissileBase>(); if (missile == null) { return(false); } if (BDArmorySettings.MULTIPLAYER_VESSELS_OWNED.Contains(message.VesselId)) { return(true); } if (missile.SourceVessel != null && BDArmorySettings.MULTIPLAYER_VESSELS_OWNED.Contains(missile.SourceVessel.id)) { return(true); } missile.Team = BDTeam.Get(message.TeamName); missile.ActivateMissileMultiplayer(); return(true); }
public static Vector3 GetAirToAirFireSolution(MissileBase missile, Vector3 targetPosition, Vector3 targetVelocity) { float leadTime = 0; float targetDistance = Vector3.Distance(targetPosition, missile.transform.position); float optSpeed = 400; //TODO: Add parameter MissileLauncher launcher = missile as MissileLauncher; if (launcher != null) { optSpeed = launcher.optimumAirspeed; } Vector3 simMissileVel = optSpeed * (targetPosition - missile.transform.position).normalized; leadTime = targetDistance / (targetVelocity - simMissileVel).magnitude; leadTime = Mathf.Clamp(leadTime, 0f, 8f); targetPosition = targetPosition + (targetVelocity * leadTime); return(targetPosition); }
private void MakeDecisionAboutThrottle(MissileBase missile) { const double maxError = 10; _futureSpeed = CalculateFutureSpeed(); var currentSpeedDelta = missile.vessel.horizontalSrfSpeed - _missile.CruiseSpeed; if (_futureSpeed > missile.CruiseSpeed) { ThrottleDecision = ThrottleDecision.Decrease; } else if (Math.Abs(_futureSpeed - _missile.CruiseSpeed) < maxError) { ThrottleDecision = ThrottleDecision.Hold; } else { ThrottleDecision = ThrottleDecision.Increase; } switch (ThrottleDecision) { case ThrottleDecision.Increase: missile.Throttle = Mathf.Clamp(missile.Throttle + 0.001f, 0, 1f); break; case ThrottleDecision.Decrease: missile.Throttle = Mathf.Clamp(missile.Throttle - 0.001f, 0, 1f); break; case ThrottleDecision.Hold: break; } _lastSpeedDelta = currentSpeedDelta; }
///// <summary> ///// Gets the laser target painter with the least angle off boresight. Set the missileBase as the reference missilePosition. ///// </summary> ///// <returns>The laser target painter.</returns> ///// <param name="referenceTransform">Reference missilePosition.</param> ///// <param name="maxBoreSight">Max bore sight.</param> //public static ModuleTargetingCamera GetLaserTarget(MissileLauncher ml, bool parentOnly) //{ // return GetModuleTargeting(parentOnly, ml.transform.forward, ml.transform.position, ml.maxOffBoresight, ml.vessel, ml.SourceVessel); // } // public static ModuleTargetingCamera GetLaserTarget(BDModularGuidance ml, bool parentOnly) // { // float maxOffBoresight = 45; // return GetModuleTargeting(parentOnly, ml.MissileReferenceTransform.forward, ml.MissileReferenceTransform.position, maxOffBoresight,ml.vessel,ml.SourceVessel); // } /// <summary> /// Gets the laser target painter with the least angle off boresight. Set the missileBase as the reference missilePosition. /// </summary> /// <returns>The laser target painter.</returns> public static ModuleTargetingCamera GetLaserTarget(MissileBase ml, bool parentOnly) { return(GetModuleTargeting(parentOnly, ml.GetForwardTransform(), ml.MissileReferenceTransform.position, ml.maxOffBoresight, ml.vessel, ml.SourceVessel)); }
public static void DoExplosionRay(Ray ray, float power, float heat, float maxDistance, ref List <Part> ignoreParts, ref List <DestructibleBuilding> ignoreBldgs, Vessel sourceVessel = null) { RaycastHit rayHit; //KerbalEVA hitEVA = null; //if (Physics.Raycast(ray, out rayHit, maxDistance, 2228224)) //{ // float sqrDist = (rayHit.point - ray.origin).sqrMagnitude; // float sqrMaxDist = maxDistance * maxDistance; // float distanceFactor = Mathf.Clamp01((sqrMaxDist - sqrDist) / sqrMaxDist); // try // { // hitEVA = rayHit.collider.gameObject.GetComponentUpwards<KerbalEVA>(); // if (hitEVA != null) // Debug.Log("[BDArmory]:Hit on kerbal confirmed!"); // } // catch (System.NullReferenceException) // { // Debug.Log("[BDArmory]:Whoops ran amok of the exception handler"); // } // Part part = hitEVA.part; // Vessel missileSource = null; // if (sourceVessel != null) // { // MissileBase ml = part.FindModuleImplementing<MissileBase>(); // if (ml) // { // missileSource = ml.SourceVessel; // } // } // if (!ignoreParts.Contains(part) && part.physicalSignificance == Part.PhysicalSignificance.FULL && // (!sourceVessel || sourceVessel != missileSource)) // { // ignoreParts.Add(part); // Rigidbody rb = part.GetComponent<Rigidbody>(); // if (rb) // { // rb.AddForceAtPosition(ray.direction * power * distanceFactor * ExplosionImpulseMultiplier, // rayHit.point, ForceMode.Impulse); // } // if (heat < 0) // { // heat = power; // } // float heatDamage = (BDArmorySettings.DMG_MULTIPLIER / 100) * ExplosionHeatMultiplier * heat * // distanceFactor / part.crashTolerance; // float excessHeat = Mathf.Max(0, (float)(part.temperature + heatDamage - part.maxTemp)); // part.AddDamage(heatDamage); // if (BDArmorySettings.DRAW_DEBUG_LABELS) // Debug.Log("[BDArmory]:====== Explosion ray hit part! Damage: " + heatDamage); // if (excessHeat > 0 && part.parent) // { // part.parent.AddDamage(excessHeat); // } // return; // } //} if (Physics.Raycast(ray, out rayHit, maxDistance, 688129)) { float sqrDist = (rayHit.point - ray.origin).sqrMagnitude; float sqrMaxDist = maxDistance * maxDistance; float distanceFactor = Mathf.Clamp01((sqrMaxDist - sqrDist) / sqrMaxDist); //parts KerbalEVA eva = rayHit.collider.gameObject.GetComponentUpwards <KerbalEVA>(); Part part = eva ? eva.part : rayHit.collider.GetComponentInParent <Part>(); if (part) { Vessel missileSource = null; if (sourceVessel != null) { MissileBase ml = part.FindModuleImplementing <MissileBase>(); if (ml) { missileSource = ml.SourceVessel; } } if (!ignoreParts.Contains(part) && part.physicalSignificance == Part.PhysicalSignificance.FULL && (!sourceVessel || sourceVessel != missileSource)) { ignoreParts.Add(part); Rigidbody rb = part.GetComponent <Rigidbody>(); if (rb) { rb.AddForceAtPosition(ray.direction * power * distanceFactor * ExplosionImpulseMultiplier, rayHit.point, ForceMode.Impulse); } if (heat < 0) { heat = power; } float heatDamage = (BDArmorySettings.DMG_MULTIPLIER / 100) * ExplosionHeatMultiplier * heat * distanceFactor / part.crashTolerance; float excessHeat = Mathf.Max(0, (float)(part.temperature + heatDamage - part.maxTemp)); part.AddDamage(heatDamage); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory]:====== Explosion ray hit part! Damage: " + heatDamage); } if (excessHeat > 0 && part.parent) { part.parent.AddDamage(excessHeat); } return; } } //buildings DestructibleBuilding building = rayHit.collider.GetComponentInParent <DestructibleBuilding>(); if (building && !ignoreBldgs.Contains(building)) { ignoreBldgs.Add(building); float damageToBuilding = (BDArmorySettings.DMG_MULTIPLIER / 100) * ExplosionHeatMultiplier * 0.00645f * power * distanceFactor; if (damageToBuilding > building.impactMomentumThreshold / 10) { building.AddDamage(damageToBuilding); } if (building.Damage > building.impactMomentumThreshold) { building.Demolish(); } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory]:== Explosion hit destructible building! Damage: " + (damageToBuilding).ToString("0.00") + ", total Damage: " + building.Damage); } } } }
public Vector3 GetDirection(MissileBase missile, Vector3 targetPosition, Vector3 targetVelocity) { //set up if (_missile.TimeIndex < 1) { return(_missile.vessel.CoM + _missile.vessel.Velocity() * 10); } upDirection = VectorUtils.GetUpDirection(_missile.vessel.CoM); planarDirectionToTarget = Vector3.ProjectOnPlane(targetPosition - _missile.vessel.CoM, upDirection).normalized; // Ascending _missile.debugString.Append("State=" + GuidanceState); _missile.debugString.Append(Environment.NewLine); var missileAltitude = GetCurrentAltitude(_missile.vessel); _missile.debugString.Append("Altitude=" + missileAltitude); _missile.debugString.Append(Environment.NewLine); _missile.debugString.Append("Apoapsis=" + _missile.vessel.orbit.ApA); _missile.debugString.Append(Environment.NewLine); _missile.debugString.Append("Future Altitude=" + _futureAltitude); _missile.debugString.Append(Environment.NewLine); _missile.debugString.Append("Pitch angle=" + _pitchAngle); _missile.debugString.Append(Environment.NewLine); _missile.debugString.Append("Pitch decision=" + PitchDecision); _missile.debugString.Append(Environment.NewLine); _missile.debugString.Append("lastVerticalSpeed=" + _lastVerticalSpeed); _missile.debugString.Append(Environment.NewLine); _missile.debugString.Append("verticalAcceleration=" + _verticalAcceleration); _missile.debugString.Append(Environment.NewLine); GetTelemetryData(); switch (GuidanceState) { case GuidanceState.Ascending: UpdateThrottle(); if (MissileWillReachAltitude(missileAltitude)) { _pitchAngle = 0; GuidanceState = GuidanceState.Cruising; break; } CheckIfTerminal(missileAltitude, targetPosition, upDirection); return(_missile.vessel.CoM + (planarDirectionToTarget.normalized + upDirection.normalized) * 10f); case GuidanceState.Cruising: CheckIfTerminal(missileAltitude, targetPosition, upDirection); //Altitude control UpdatePitch(missileAltitude); UpdateThrottle(); return(_missile.vessel.CoM + 10 * planarDirectionToTarget.normalized + _pitchAngle * upDirection); case GuidanceState.Terminal: _missile.debugString.Append($"Descending"); _missile.debugString.Append(Environment.NewLine); _missile.Throttle = Mathf.Clamp((float)(_missile.vessel.atmDensity * 10f), 0.01f, 1f); if (_missile is BDModularGuidance) { if (_missile.vessel.InVacuum()) { return(_missile.vessel.CoM + _missile.vessel.Velocity() * 10); } } return(MissileGuidance.GetAirToGroundTarget(targetPosition, targetVelocity, _missile.vessel, 1.85f)); } return(_missile.vessel.CoM + _missile.vessel.Velocity() * 10); }
public CruiseGuidance(MissileBase missile) { _missile = missile; }
/// <summary> /// Gets the dynamic launch parameters. /// </summary> /// <returns>The dynamic launch parameters.</returns> /// <param name="launcherVelocity">Launcher velocity.</param> /// <param name="targetVelocity">Target velocity.</param> public static MissileLaunchParams GetDynamicLaunchParams(MissileBase missile, Vector3 targetVelocity, Vector3 targetPosition) { Vector3 launcherVelocity = missile.vessel.Velocity(); float launcherSpeed = (float)missile.vessel.srfSpeed; float minLaunchRange = missile.minStaticLaunchRange; float maxLaunchRange = missile.maxStaticLaunchRange; float bodyGravity = (float)PhysicsGlobals.GravitationalAcceleration * (float)missile.vessel.orbit.referenceBody.GeeASL; // Set gravity for calculations; float missileActiveTime = 2f; float rangeAddMin = 0; float rangeAddMax = 0; float relSpeed; // Calculate relative speed Vector3 relV = targetVelocity - launcherVelocity; Vector3 vectorToTarget = targetPosition - missile.part.transform.position; Vector3 relVProjected = Vector3.Project(relV, vectorToTarget); relSpeed = -Mathf.Sign(Vector3.Dot(relVProjected, vectorToTarget)) * relVProjected.magnitude; if (missile.GetComponent <BDModularGuidance>() == null) { // Basic time estimate for missile to drop and travel a safe distance from vessel assuming constant acceleration and firing vessel not accelerating MissileLauncher ml = missile.GetComponent <MissileLauncher>(); float maxMissileAccel = ml.thrust / missile.part.mass; float blastRadius = Mathf.Min(missile.GetBlastRadius(), 150f); // Allow missiles with absurd blast ranges to still be launched if desired missileActiveTime = Mathf.Min((missile.vessel.LandedOrSplashed ? 0f : missile.dropTime) + Mathf.Sqrt(2 * blastRadius / maxMissileAccel), 2f); // Clamp at 2s for now // Rough range estimate of max missile G in a turn after launch, the following code is quite janky but works decently well in practice float maxEstimatedGForce = Mathf.Max(bodyGravity * ml.maxTorque, 15f); // Rough estimate of max G based on missile torque, use minimum of 15G to prevent some VLS parts from not working if (ml.aero) // If missile has aerodynamics, modify G force by AoA limit { maxEstimatedGForce *= Mathf.Sin(ml.maxAoA * Mathf.Deg2Rad); } // Rough estimate of turning radius and arc length to travel float arcLength = 0; if ((!missile.vessel.LandedOrSplashed) && (missile.GetWeaponClass() != WeaponClasses.SLW)) // If the missile isn't a torpedo { float futureTime = Mathf.Clamp((missile.vessel.LandedOrSplashed ? 0f : missile.dropTime), 0f, 2f); Vector3 futureRelPosition = (targetPosition + targetVelocity * futureTime) - (missile.part.transform.position + launcherVelocity * futureTime); float missileTurnRadius = (ml.optimumAirspeed * ml.optimumAirspeed) / maxEstimatedGForce; float targetAngle = Vector3.Angle(missile.GetForwardTransform(), futureRelPosition); arcLength = Mathf.Deg2Rad * targetAngle * missileTurnRadius; } // Add additional range term for the missile to manuever to target at missileActiveTime rangeAddMin += arcLength; } float missileMaxRangeTime = 8f; // Placeholder value since this doesn't really matter much in BDA combat // Add to ranges rangeAddMin += relSpeed * missileActiveTime; rangeAddMax += relSpeed * missileMaxRangeTime; // Add altitude term to max double diffAlt = missile.vessel.altitude - FlightGlobals.getAltitudeAtPos(targetPosition); rangeAddMax += (float)diffAlt; float min = Mathf.Clamp(minLaunchRange + rangeAddMin, 0, BDArmorySettings.MAX_ENGAGEMENT_RANGE); float max = Mathf.Clamp(maxLaunchRange + rangeAddMax, min + 100, BDArmorySettings.MAX_ENGAGEMENT_RANGE); return(new MissileLaunchParams(min, max)); }
private double CalculateFutureHorizontalSpeed(MissileBase missile, int predictionTime = 10) { return(missile.vessel.horizontalSrfSpeed + (missile.HorizontalAcceleration / Time.fixedDeltaTime) * predictionTime); }
void Awake() { if (!vessel) { vessel = GetComponent <Vessel>(); } if (!vessel) { //Debug.Log ("[BDArmory]: TargetInfo was added to a non-vessel"); Destroy(this); return; } //destroy this if a target info is already attached to the vessel IEnumerator otherInfo = vessel.gameObject.GetComponents <TargetInfo>().GetEnumerator(); while (otherInfo.MoveNext()) { if ((object)otherInfo.Current != this) { Destroy(this); return; } } Team = null; bool foundMf = false; List <MissileFire> .Enumerator mf = vessel.FindPartModulesImplementing <MissileFire>().GetEnumerator(); while (mf.MoveNext()) { foundMf = true; Team = mf.Current.Team; weaponManager = mf.Current; break; } mf.Dispose(); if (!foundMf) { List <MissileBase> .Enumerator ml = vessel.FindPartModulesImplementing <MissileBase>().GetEnumerator(); while (ml.MoveNext()) { isMissile = true; MissileBaseModule = ml.Current; Team = ml.Current.Team; break; } ml.Dispose(); } vessel.OnJustAboutToBeDestroyed += AboutToBeDestroyed; //add delegate to peace enable event BDArmorySetup.OnPeaceEnabled += OnPeaceEnabled; //lifeRoutine = StartCoroutine(LifetimeRoutine()); // TODO: CHECK BEHAVIOUR AND SIDE EFFECTS! if (!isMissile && Team != null) { GameEvents.onVesselPartCountChanged.Add(VesselModified); //massRoutine = StartCoroutine(MassRoutine()); // TODO: CHECK BEHAVIOUR AND SIDE EFFECTS! } }
private double CalculateFutureVerticalSpeed(MissileBase missile, int predictionTime = 10) { return(missile.vessel.verticalSpeed + (missile.VerticalAcceleration / Time.fixedDeltaTime) * predictionTime); }
public Vector3 GetDirection(MissileBase missile, Vector3 targetPosition) { //set up if (_originalDistance == float.MinValue) { _startPoint = missile.vessel.CoM; _originalDistance = Vector3.Distance(targetPosition, missile.vessel.CoM); } var surfaceDistanceVector = Vector3 .Project((missile.vessel.CoM - _startPoint), (targetPosition - _startPoint).normalized); var pendingDistance = _originalDistance - surfaceDistanceVector.magnitude; if (missile.TimeIndex < 1) { return(missile.vessel.CoM + missile.vessel.Velocity() * 10); } Vector3 agmTarget; if (missile.vessel.verticalSpeed > 0 && pendingDistance > _originalDistance * 0.5) { missile.debugString.Append($"Ascending"); missile.debugString.Append(Environment.NewLine); var freeFallTime = CalculateFreeFallTime(missile); missile.debugString.Append($"freeFallTime: {freeFallTime}"); missile.debugString.Append(Environment.NewLine); var futureDistanceVector = Vector3 .Project((missile.vessel.GetFuturePosition() - _startPoint), (targetPosition - _startPoint).normalized); var futureHorizontalSpeed = CalculateFutureHorizontalSpeed(missile); var horizontalTime = (_originalDistance - futureDistanceVector.magnitude) / futureHorizontalSpeed; missile.debugString.Append($"horizontalTime: {horizontalTime}"); missile.debugString.Append(Environment.NewLine); if (freeFallTime >= horizontalTime) { missile.debugString.Append($"Free fall achieved:"); missile.debugString.Append(Environment.NewLine); missile.Throttle = Mathf.Clamp(missile.Throttle - 0.001f, 0.01f, 1f); } else { missile.debugString.Append($"Free fall not achieved:"); missile.debugString.Append(Environment.NewLine); missile.Throttle = Mathf.Clamp(missile.Throttle + 0.001f, 0.01f, 1f); } Vector3 dToTarget = targetPosition - missile.vessel.CoM; Vector3 direction = Quaternion.AngleAxis(Mathf.Clamp(missile.maxOffBoresight * 0.9f, 0, missile.BallisticAngle), Vector3.Cross(dToTarget, VectorUtils.GetUpDirection(missile.vessel.CoM))) * dToTarget; agmTarget = missile.vessel.CoM + direction; missile.debugString.Append($"Throttle: {missile.Throttle}"); missile.debugString.Append(Environment.NewLine); } else { missile.debugString.Append($"Descending"); missile.debugString.Append(Environment.NewLine); agmTarget = MissileGuidance.GetAirToGroundTarget(targetPosition, missile.vessel, 1.85f); missile.Throttle = Mathf.Clamp((float)(missile.vessel.atmDensity * 10f), 0.01f, 1f); } if (missile is BDModularGuidance) { if (missile.vessel.InVacuum()) { missile.vessel.Autopilot.SetMode(VesselAutopilot.AutopilotMode.Prograde); agmTarget = missile.vessel.CoM + missile.vessel.Velocity() * 100; } else { missile.vessel.Autopilot.SetMode(VesselAutopilot.AutopilotMode.StabilityAssist); } } return(agmTarget); }