/// <summary> /// Update vessel /// </summary> /// <param name="currentTime"></param> internal override void Update(double currentTime) { if (vessel == null) { return; } if (vessel.isActiveVessel) { lastTimeUpdated = 0; if (active) { ScreenMessages.PostScreenMessage(Localizer.Format("#LOC_BV_AutopilotActive"), 10f).color = Color.red; } return; } if (!active || vessel.loaded) { return; } // If we don't know the last time of update, then set it and wait for the next update cycle if (lastTimeUpdated == 0) { State = VesselState.Idle; lastTimeUpdated = currentTime; BVModule.SetValue("lastTimeUpdated", currentTime.ToString()); return; } Vector3d roverPos = vessel.mainBody.position - vessel.GetWorldPos3D(); Vector3d toMainStar = vessel.mainBody.position - FlightGlobals.Bodies[mainStarIndex].position; angle = Vector3d.Angle(roverPos, toMainStar); // Angle between rover and the main star // Speed penalties at twighlight and at night if ((angle > 90) && manned) // night { speedMultiplier = 0.25; } else if ((angle > 85) && manned) // twilight { speedMultiplier = 0.5; } else if ((angle > 80) && manned) // twilight { speedMultiplier = 0.75; } else // day { speedMultiplier = 1.0; } double deltaT = currentTime - lastTimeUpdated; // Time delta from the last update double deltaTOver = 0; // deltaT which is calculated from a value over the maximum resource amout available // Compute increase or decrease in EC from the last update if (batteries.UseBatteries) { // Process fuel cells before batteries if (fuelCells.Use && ((angle > 90) || (batteries.ECPerSecondGenerated - fuelCells.OutputValue <= 0) || (batteries.CurrentEC < batteries.MaxUsedEC))) // Night, not enough solar power or we need to recharge batteries { var iList = fuelCells.InputResources; for (int i = 0; i < iList.Count; i++) { iList[i].CurrentAmountUsed += iList[i].Ratio * deltaT; if (iList[i].CurrentAmountUsed > iList[i].MaximumAmountAvailable) { deltaTOver = Math.Max(deltaTOver, (iList[i].CurrentAmountUsed - iList[i].MaximumAmountAvailable) / iList[i].Ratio); } } if (deltaTOver > 0) { deltaT -= deltaTOver; // Reduce the amount of used resources for (int i = 0; i < iList.Count; i++) { iList[i].CurrentAmountUsed -= iList[i].Ratio * deltaTOver; } } } if (angle <= 90) // day { batteries.CurrentEC = Math.Min(batteries.CurrentEC + batteries.ECPerSecondGenerated * deltaT, batteries.MaxUsedEC); } else // night { batteries.CurrentEC = Math.Max(batteries.CurrentEC - batteries.ECPerSecondConsumed * deltaT, 0); } } // No moving at night, if there isn't enough power if ((angle > 90) && (averageSpeedAtNight == 0.0) && !(batteries.UseBatteries && (batteries.CurrentEC > 0))) { State = VesselState.AwaitingSunlight; lastTimeUpdated = currentTime; BVModule.SetValue("lastTimeUpdated", currentTime.ToString()); return; } double deltaS = AverageSpeed * deltaT; // Distance delta from the last update distanceTravelled += deltaS; if (distanceTravelled >= distanceToTarget) // We reached the target { if (!MoveSafely(targetLatitude, targetLongitude)) { distanceTravelled -= deltaS; } else { distanceTravelled = distanceToTarget; active = false; arrived = true; BVModule.SetValue("active", "False"); BVModule.SetValue("arrived", "True"); BVModule.SetValue("distanceTravelled", distanceToTarget.ToString()); BVModule.SetValue("pathEncoded", ""); // Dewarp if (Configuration.AutomaticDewarp) { if (TimeWarp.CurrentRate > 3) // Instant drop to 50x warp { TimeWarp.SetRate(3, true); } if (TimeWarp.CurrentRate > 0) // Gradual drop out of warp { TimeWarp.SetRate(0, false); } ScreenMessages.PostScreenMessage(vessel.vesselName + " " + Localizer.Format("#LOC_BV_VesselArrived") + " " + vessel.mainBody.bodyDisplayName.Replace("^N", ""), 5f); } NotifyArrival(); } State = VesselState.Idle; } else { try // There is sometimes null ref exception during scene change { int step = Convert.ToInt32(Math.Floor(distanceTravelled / PathFinder.StepSize)); // In which step of the path we are double remainder = distanceTravelled % PathFinder.StepSize; // Current remaining distance from the current step double bearing = 0; if (step < path.Count - 1) { bearing = GeoUtils.InitialBearing( // Bearing to the next step from previous step path[step].latitude, path[step].longitude, path[step + 1].latitude, path[step + 1].longitude ); } else { bearing = GeoUtils.InitialBearing( // Bearing to the target from previous step path[step].latitude, path[step].longitude, targetLatitude, targetLongitude ); } // Compute new coordinates, we are moving from the current step, distance is "remainder" double[] newCoordinates = GeoUtils.GetLatitudeLongitude( path[step].latitude, path[step].longitude, bearing, remainder, vessel.mainBody.Radius ); // Move if (!MoveSafely(newCoordinates[0], newCoordinates[1])) { distanceTravelled -= deltaS; State = VesselState.Idle; } else { State = VesselState.Moving; } } catch { } } Save(currentTime); // Stop the rover, we don't have enough of fuel if (deltaTOver > 0) { active = false; arrived = true; BVModule.SetValue("active", "False"); BVModule.SetValue("arrived", "True"); BVModule.SetValue("pathEncoded", ""); // Dewarp if (Configuration.AutomaticDewarp) { if (TimeWarp.CurrentRate > 3) // Instant drop to 50x warp { TimeWarp.SetRate(3, true); } if (TimeWarp.CurrentRate > 0) // Gradual drop out of warp { TimeWarp.SetRate(0, false); } ScreenMessages.PostScreenMessage(vessel.vesselName + " " + Localizer.Format("#LOC_BV_Warning_Stopped") + ". " + Localizer.Format("#LOC_BV_Warning_NotEnoughFuel"), 5f).color = Color.red; } NotifyNotEnoughFuel(); State = VesselState.Idle; } }
/// <summary> /// Constructor /// </summary> /// <param name="v"></param> /// <param name="module"></param> internal RoverController(Vessel v, ConfigNode module) : base(v, module) { // Load values from config if it isn't the first run of the mod (we are reseting vessel on the first run) if (!Configuration.FirstRun) { averageSpeed = double.Parse(BVModule.GetValue("averageSpeed") != null ? BVModule.GetValue("averageSpeed") : "0"); averageSpeedAtNight = double.Parse(BVModule.GetValue("averageSpeedAtNight") != null ? BVModule.GetValue("averageSpeedAtNight") : "0"); manned = bool.Parse(BVModule.GetValue("manned") != null ? BVModule.GetValue("manned") : "false"); vesselHeightFromTerrain = double.Parse(BVModule.GetValue("vesselHeightFromTerrain") != null ? BVModule.GetValue("vesselHeightFromTerrain") : "0"); } speedMultiplier = 1.0; angle = 0; maxSpeedBase = 0; wheelsPercentualModifier = 0; crewSpeedBonus = 0; }
/// <summary> /// Update vessel /// </summary> /// <param name="currentTime"></param> internal override void Update(double currentTime) { if (vessel == null) { return; } if (vessel.isActiveVessel) { lastTimeUpdated = 0; if (active) { ScreenMessages.PostScreenMessage(Localizer.Format("#LOC_BV_AutopilotActive"), 10f).color = Color.red; } return; } if (!active || vessel.loaded) { return; } // If we don't know the last time of update, then set it and wait for the next update cycle if (lastTimeUpdated == 0) { State = VesselState.Idle; lastTimeUpdated = currentTime; BVModule.SetValue("lastTimeUpdated", currentTime.ToString()); return; } Vector3d shipPos = vessel.mainBody.position - vessel.GetWorldPos3D(); Vector3d toMainStar = vessel.mainBody.position - FlightGlobals.Bodies[mainStarIndex].position; angle = Vector3d.Angle(shipPos, toMainStar); // Angle between rover and the main star // Speed penalties at twighlight and at night if ((angle > 90) && manned) // night { speedMultiplier = 0.25; } else if ((angle > 85) && manned) // twilight { speedMultiplier = 0.5; } else if ((angle > 80) && manned) // twilight { speedMultiplier = 0.75; } else // day { speedMultiplier = 1.0; } double deltaT = currentTime - lastTimeUpdated; // Time delta from the last update double deltaTOver = 0; // deltaT which is calculated from a value over the maximum propellant amout available // No moving at night, if there isn't enough power if ((angle > 90) && (averageSpeedAtNight == 0.0)) { State = VesselState.AwaitingSunlight; lastTimeUpdated = currentTime; BVModule.SetValue("lastTimeUpdated", currentTime.ToString()); return; } if (!CheatOptions.InfinitePropellant) { for (int i = 0; i < propellants.Count; i++) { propellants[i].CurrentAmountUsed += propellants[i].FuelFlow * deltaT * speedMultiplier * speedMultiplier; // If speed is reduced, then thrust and subsequently fuel flow are reduced by square (from drag equation) if (propellants[i].CurrentAmountUsed > propellants[i].MaximumAmountAvailable) { deltaTOver = Math.Max(deltaTOver, (propellants[i].CurrentAmountUsed - propellants[i].MaximumAmountAvailable) / (propellants[i].FuelFlow * speedMultiplier * speedMultiplier)); } } if (deltaTOver > 0) { deltaT -= deltaTOver; // Reduce the amount of used propellants for (int i = 0; i < propellants.Count; i++) { propellants[i].CurrentAmountUsed -= propellants[i].FuelFlow * deltaTOver * speedMultiplier * speedMultiplier; } } } double deltaS = AverageSpeed * deltaT; // Distance delta from the last update distanceTravelled += deltaS; if (distanceTravelled >= distanceToTarget) // We reached the target { if (!MoveSafely(targetLatitude, targetLongitude)) { distanceTravelled -= deltaS; } else { distanceTravelled = distanceToTarget; active = false; arrived = true; BVModule.SetValue("active", "False"); BVModule.SetValue("arrived", "True"); BVModule.SetValue("distanceTravelled", distanceToTarget.ToString()); BVModule.SetValue("pathEncoded", ""); // Dewarp if (Configuration.AutomaticDewarp) { if (TimeWarp.CurrentRate > 3) // Instant drop to 50x warp { TimeWarp.SetRate(3, true); } if (TimeWarp.CurrentRate > 0) // Gradual drop out of warp { TimeWarp.SetRate(0, false); } ScreenMessages.PostScreenMessage(vessel.vesselName + " " + Localizer.Format("#LOC_BV_VesselArrived") + " " + vessel.mainBody.bodyDisplayName.Replace("^N", ""), 5f); } NotifyArrival(); } State = VesselState.Idle; } else { try // There is sometimes null ref exception during scene change { int step = Convert.ToInt32(Math.Floor(distanceTravelled / PathFinder.StepSize)); // In which step of the path we are double remainder = distanceTravelled % PathFinder.StepSize; // Current remaining distance from the current step double bearing = 0; if (step < path.Count - 1) { bearing = GeoUtils.InitialBearing( // Bearing to the next step from previous step path[step].latitude, path[step].longitude, path[step + 1].latitude, path[step + 1].longitude ); } else { bearing = GeoUtils.InitialBearing( // Bearing to the target from previous step path[step].latitude, path[step].longitude, targetLatitude, targetLongitude ); } // Compute new coordinates, we are moving from the current step, distance is "remainder" double[] newCoordinates = GeoUtils.GetLatitudeLongitude( path[step].latitude, path[step].longitude, bearing, remainder, vessel.mainBody.Radius ); // Move if (!MoveSafely(newCoordinates[0], newCoordinates[1])) { distanceTravelled -= deltaS; State = VesselState.Idle; } else { State = VesselState.Moving; } } catch { } } Save(currentTime); // Stop the ship, we don't have enough of propellant if (deltaTOver > 0) { active = false; arrived = true; BVModule.SetValue("active", "False"); BVModule.SetValue("arrived", "True"); BVModule.SetValue("pathEncoded", ""); // Dewarp if (Configuration.AutomaticDewarp) { if (TimeWarp.CurrentRate > 3) // Instant drop to 50x warp { TimeWarp.SetRate(3, true); } if (TimeWarp.CurrentRate > 0) // Gradual drop out of warp { TimeWarp.SetRate(0, false); } ScreenMessages.PostScreenMessage(vessel.vesselName + " " + Localizer.Format("#LOC_BV_Warning_Stopped") + ". " + Localizer.Format("#LOC_BV_Warning_NotEnoughFuel"), 5f).color = Color.red; } NotifyNotEnoughFuel(); State = VesselState.Idle; } }