void SetOrbitHere(Entity entity, PositionDB positionDB, WarpMovingDB moveDB, DateTime atDateTime) { //propulsionDB.CurrentVectorMS = new Vector3(0, 0, 0); double targetSOI = OrbitProcessor.GetSOI_m(moveDB.TargetEntity); Entity targetEntity; if (moveDB.TargetEntity.GetDataBlob <PositionDB>().GetDistanceTo_m(positionDB) > targetSOI) { targetEntity = moveDB.TargetEntity.GetDataBlob <OrbitDB>().Parent; //TODO: it's concevable we could be in another SOI not the parent (ie we could be in a target's moon's SOI) } else { targetEntity = moveDB.TargetEntity; } OrbitDB targetOrbit = targetEntity.GetDataBlob <OrbitDB>(); Vector3 insertionVector_m = OrbitProcessor.GetOrbitalInsertionVector_m(moveDB.SavedNewtonionVector, targetOrbit, atDateTime); positionDB.SetParent(targetEntity); if (moveDB.ExpendDeltaV.Length() != 0) { NewtonThrustCommand.CreateCommand(entity.FactionOwner, entity, entity.StarSysDateTime, moveDB.ExpendDeltaV); entity.RemoveDataBlob <WarpMovingDB>(); moveDB.IsAtTarget = true; } else { OrbitDB newOrbit = OrbitDB.FromVelocity_m(targetEntity, entity, insertionVector_m, atDateTime); entity.RemoveDataBlob <WarpMovingDB>(); if (newOrbit.Apoapsis < targetSOI) //furtherst point within soi, normal orbit { entity.SetDataBlob(newOrbit); } else if (newOrbit.Periapsis > targetSOI) //closest point outside soi { //find who's SOI we are in, and create an orbit around that. targetEntity = OrbitProcessor.FindSOIForPosition((StarSystem)entity.Manager, positionDB.AbsolutePosition_m); newOrbit = OrbitDB.FromVelocity_m(targetEntity, entity, insertionVector_m, atDateTime); entity.SetDataBlob(newOrbit); } else //closest point inside soi, but furtherest point outside. make a newtonion trajectory. { var newtmove = new NewtonMoveDB(targetEntity, insertionVector_m); entity.SetDataBlob(newtmove); } positionDB.SetParent(targetEntity); moveDB.IsAtTarget = true; } }
void SetOrbitHere(Entity entity, PropulsionAbilityDB propulsionDB, PositionDB positionDB, TranslateMoveDB moveDB, DateTime atDateTime) { propulsionDB.CurrentVectorMS = new Vector3(0, 0, 0); double targetSOI = OrbitProcessor.GetSOI(moveDB.TargetEntity); Entity targetEntity; if (moveDB.TargetEntity.GetDataBlob <PositionDB>().GetDistanceTo(positionDB) > targetSOI) { targetEntity = moveDB.TargetEntity.GetDataBlob <OrbitDB>().Parent; //TODO: it's concevable we could be in another SOI not the parent (ie we could be in a target's moon's SOI) } else { targetEntity = moveDB.TargetEntity; } OrbitDB targetOrbit = targetEntity.GetDataBlob <OrbitDB>(); var orbitalVector = OrbitProcessor.GetOrbitalVector(targetOrbit, atDateTime); var insertionVector2d = OrbitProcessor.GetOrbitalInsertionVector(moveDB.SavedNewtonionVector_AU, targetOrbit, atDateTime); Vector3 parentOrbitalVector = new Vector3(orbitalVector.X, orbitalVector.Y, 0); Vector3 insertionVector = new Vector3(insertionVector2d.X, insertionVector2d.Y, 0); insertionVector += moveDB.ExpendDeltaV_AU; //TODO: only use it if we have it. propulsionDB.RemainingDV_MS -= (float)Distance.AuToMt(moveDB.ExpendDeltaV_AU).Length(); OrbitDB newOrbit = OrbitDB.FromVector(targetEntity, entity, insertionVector, atDateTime); if (newOrbit.Periapsis > targetSOI) { //TODO: find who's SOI we're currently in and create an orbit for that; } if (newOrbit.Apoapsis > targetSOI) { //TODO: change orbit to new parent at SOI change } positionDB.SetParent(targetEntity); moveDB.IsAtTarget = true; entity.RemoveDataBlob <TranslateMoveDB>(); entity.SetDataBlob(newOrbit); newOrbit.SetParent(targetEntity); }
/// <summary> /// This was designed so that fast moving objects will get interpolated a lot more than slow moving objects /// so fast moving objects shouldn't loose positional acuracy when close to a planet, /// and slow moving objects won't have processor time wasted on them by calulcating too often. /// However this seems to be unstable and looses energy, unsure why. currently set it to just itterate/interpolate every second. /// so currently will be using more time to get through this than neccisary. /// </summary> /// <param name="entity">Entity.</param> /// <param name="deltaSeconds">Delta seconds.</param> public static void NewtonMove(Entity entity, int deltaSeconds) { NewtonMoveDB newtonMoveDB = entity.GetDataBlob <NewtonMoveDB>(); NewtonThrustAbilityDB newtonThrust = entity.GetDataBlob <NewtonThrustAbilityDB>(); PositionDB positionDB = entity.GetDataBlob <PositionDB>(); double mass_Kg = entity.GetDataBlob <MassVolumeDB>().Mass; double parentMass_kg = newtonMoveDB.ParentMass; var manager = entity.Manager; DateTime dateTimeFrom = newtonMoveDB.LastProcessDateTime; DateTime dateTimeNow = manager.ManagerSubpulses.StarSysDateTime; DateTime dateTimeFuture = dateTimeNow + TimeSpan.FromSeconds(deltaSeconds); double deltaT = (dateTimeFuture - dateTimeFrom).TotalSeconds; double secondsToItterate = deltaT; while (secondsToItterate > 0) { //double timeStep = Math.Max(secondsToItterate / speed_kms, 1); //timeStep = Math.Min(timeStep, secondsToItterate); double timeStepInSeconds = 1;//because the above seems unstable and looses energy. double distanceToParent_m = positionDB.GetDistanceTo_m(newtonMoveDB.SOIParent.GetDataBlob <PositionDB>()); distanceToParent_m = Math.Max(distanceToParent_m, 0.1); //don't let the distance be 0 (once collision is in this will likely never happen anyway) double gravForce = GameConstants.Science.GravitationalConstant * (mass_Kg * parentMass_kg / Math.Pow(distanceToParent_m, 2)); Vector3 gravForceVector = gravForce * -Vector3.Normalise(positionDB.RelativePosition_m); Vector3 totalDVFromGrav = (gravForceVector / mass_Kg) * timeStepInSeconds; double maxAccelFromThrust1 = newtonThrust.ExhaustVelocity * Math.Log(mass_Kg / (mass_Kg - newtonThrust.FuelBurnRate)); //per second double maxAccelFromThrust = newtonThrust.ThrustInNewtons / mass_Kg; //per second Vector3 manuverDV = newtonMoveDB.DeltaVForManuver_m; //how much dv needed to complete the manuver. double dryMass = mass_Kg - newtonThrust.FuelBurnRate * timeStepInSeconds; //how much our ship weighs after a timestep of fuel is used. //how much dv can we get in this timestep. double deltaVThisStep = OrbitMath.TsiolkovskyRocketEquation(mass_Kg, dryMass, newtonThrust.ExhaustVelocity); deltaVThisStep = Math.Min(manuverDV.Length(), deltaVThisStep); //don't use more Dv than what is called for. deltaVThisStep = Math.Min(newtonThrust.DeltaV, deltaVThisStep); //check we've got the deltaV to spend. Vector3 totalDVFromThrust = Vector3.Normalise(manuverDV) * deltaVThisStep; //remove the deltaV we're expending from the max (TODO: Remove fuel from cargo, change mass of ship) newtonThrust.DeltaV -= deltaVThisStep; //remove the vectorDV from the amount needed to fully complete the manuver. newtonMoveDB.DeltaVForManuver_m -= totalDVFromThrust; Vector3 totalDV = totalDVFromGrav + totalDVFromThrust; Vector3 newVelocity = totalDV + newtonMoveDB.CurrentVector_ms; newtonMoveDB.CurrentVector_ms = newVelocity; Vector3 deltaPos = (newtonMoveDB.CurrentVector_ms + newVelocity) / 2 * timeStepInSeconds; positionDB.RelativePosition_m += deltaPos; double sOIRadius = OrbitProcessor.GetSOI_m(newtonMoveDB.SOIParent); if (positionDB.RelativePosition_m.Length() >= sOIRadius) { Entity newParent; Vector3 parentRalitiveVector; //if our parent is a regular kepler object (normaly this is the case) if (newtonMoveDB.SOIParent.HasDataBlob <OrbitDB>()) { var orbitDB = newtonMoveDB.SOIParent.GetDataBlob <OrbitDB>(); newParent = orbitDB.Parent; var parentVelocity = OrbitProcessor.InstantaneousOrbitalVelocityVector_m(orbitDB, entity.StarSysDateTime); parentRalitiveVector = newtonMoveDB.CurrentVector_ms + parentVelocity; } else //if (newtonMoveDB.SOIParent.HasDataBlob<NewtonMoveDB>()) { //this will pretty much never happen. newParent = newtonMoveDB.SOIParent.GetDataBlob <NewtonMoveDB>().SOIParent; var parentVelocity = newtonMoveDB.SOIParent.GetDataBlob <NewtonMoveDB>().CurrentVector_ms; parentRalitiveVector = newtonMoveDB.CurrentVector_ms + parentVelocity; } parentMass_kg = newParent.GetDataBlob <MassVolumeDB>().Mass; Vector3 posRalitiveToNewParent = positionDB.AbsolutePosition_m - newParent.GetDataBlob <PositionDB>().AbsolutePosition_m; var dateTime = dateTimeNow + TimeSpan.FromSeconds(deltaSeconds - secondsToItterate); double sgp = GMath.StandardGravitationalParameter(parentMass_kg + mass_Kg); var kE = OrbitMath.KeplerFromPositionAndVelocity(sgp, posRalitiveToNewParent, parentRalitiveVector, dateTime); positionDB.SetParent(newParent); newtonMoveDB.ParentMass = parentMass_kg; newtonMoveDB.SOIParent = newParent; newtonMoveDB.CurrentVector_ms = parentRalitiveVector; } if (newtonMoveDB.DeltaVForManuver_m.Length() <= 0) //if we've completed the manuver. { var dateTime = dateTimeNow + TimeSpan.FromSeconds(deltaSeconds - secondsToItterate); double sgp = GMath.StandardGravitationalParameter(parentMass_kg + mass_Kg); KeplerElements kE = OrbitMath.KeplerFromPositionAndVelocity(sgp, positionDB.RelativePosition_m, newtonMoveDB.CurrentVector_ms, dateTime); var parentEntity = Entity.GetSOIParentEntity(entity, positionDB); if (kE.Eccentricity < 1) //if we're going to end up in a regular orbit around our new parent { var newOrbit = OrbitDB.FromKeplerElements( parentEntity, mass_Kg, kE, dateTime); entity.RemoveDataBlob <NewtonMoveDB>(); entity.SetDataBlob(newOrbit); positionDB.SetParent(parentEntity); var newPos = OrbitProcessor.GetPosition_m(newOrbit, dateTime); positionDB.RelativePosition_m = newPos; } break; } secondsToItterate -= timeStepInSeconds; } newtonMoveDB.LastProcessDateTime = dateTimeFuture; }
/// <summary> /// process PropulsionDB movement for a single system /// </summary> /// <param name="manager">the system to process</param> /// <param name="deltaSeconds">amount of time in seconds</param> internal static void Process(EntityManager manager, int deltaSeconds) { OrderProcessor.ProcessSystem(manager); foreach (Entity shipEntity in manager.GetAllEntitiesWithDataBlob <PropulsionDB>()) { PositionDB positionDB = shipEntity.GetDataBlob <PositionDB>(); PropulsionDB propulsionDB = shipEntity.GetDataBlob <PropulsionDB>(); Queue <BaseOrder> orders = shipEntity.GetDataBlob <ShipInfoDB>().Orders; if (orders.Count > 0) { if (orders.Peek().OrderType == orderType.MOVETO) { // Check to see if we will overtake the target MoveOrder order = (MoveOrder)orders.Peek(); Vector4 shipPos = positionDB.AbsolutePosition; Vector4 targetPos; Vector4 currentSpeed = shipEntity.GetDataBlob <PropulsionDB>().CurrentSpeed; Vector4 nextTPos = shipPos + (currentSpeed * deltaSeconds); Vector4 newPos = shipPos; Vector4 deltaVecToTarget; Vector4 deltaVecToNextT; double distanceToTarget; double distanceToNextTPos; double speedDelta; double distanceDelta; double newDistanceDelta; double fuelMaxDistanceAU; double currentSpeedLength = currentSpeed.Length(); CargoStorageDB storedResources = shipEntity.GetDataBlob <CargoStorageDB>(); Dictionary <Guid, double> fuelUsePerMeter = propulsionDB.FuelUsePerKM; int maxKMeters = CalcMaxFuelDistance(shipEntity); if (order.PositionTarget == null) { targetPos = order.Target.GetDataBlob <PositionDB>().AbsolutePosition; } else { targetPos = order.PositionTarget.AbsolutePosition; } deltaVecToTarget = shipPos - targetPos; distanceToTarget = deltaVecToTarget.Length(); //in au deltaVecToNextT = shipPos - nextTPos; fuelMaxDistanceAU = GameConstants.Units.KmPerAu * maxKMeters; distanceToNextTPos = deltaVecToNextT.Length(); if (fuelMaxDistanceAU < distanceToNextTPos) { newDistanceDelta = fuelMaxDistanceAU; double percent = fuelMaxDistanceAU / distanceToNextTPos; newPos = nextTPos + deltaVecToNextT * percent; Event usedAllFuel = new Event(manager.ManagerSubpulses.SystemLocalDateTime, "Used all Fuel", shipEntity.GetDataBlob <OwnedDB>().ObjectOwner, shipEntity); usedAllFuel.EventType = EventType.FuelExhausted; manager.Game.EventLog.AddEvent(usedAllFuel); } else { newDistanceDelta = distanceToNextTPos; newPos = nextTPos; } if (distanceToTarget < newDistanceDelta) // moving would overtake target, just go directly to target { newDistanceDelta = distanceToTarget; propulsionDB.CurrentSpeed = new Vector4(0, 0, 0, 0); newPos = targetPos; if (order.Target != null && order.Target.HasDataBlob <SystemBodyInfoDB>()) { positionDB.SetParent(order.Target); } if (order.Target != null) { if (order.Target.HasDataBlob <SystemBodyInfoDB>()) // Set position to the target body { positionDB.SetParent(order.Target); } } else // We arrived, get rid of the order { shipEntity.GetDataBlob <ShipInfoDB>().Orders.Dequeue(); } } positionDB.AbsolutePosition = newPos; int kMetersMoved = (int)(newDistanceDelta * GameConstants.Units.KmPerAu); Dictionary <Guid, int> fuelAmounts = new Dictionary <Guid, int>(); foreach (var item in propulsionDB.FuelUsePerKM) { fuelAmounts.Add(item.Key, (int)item.Value * kMetersMoved); } StorageSpaceProcessor.RemoveResources(storedResources, fuelAmounts); } } } }
/// <summary> /// This was designed so that fast moving objects will get interpolated a lot more than slow moving objects /// so fast moving objects shouldn't loose positional acuracy when close to a planet, /// and slow moving objects won't have processor time wasted on them by calulcating too often. /// However this seems to be unstable and looses energy, unsure why. currently set it to just itterate/interpolate every second. /// so currently will be using more time to get through this than neccisary. /// </summary> /// <param name="entity">Entity.</param> /// <param name="deltaSeconds">Delta seconds.</param> public static void NewtonMove(Entity entity, int deltaSeconds) { NewtonMoveDB newtonMoveDB = entity.GetDataBlob <NewtonMoveDB>(); PositionDB positionDB = entity.GetDataBlob <PositionDB>(); double Mass_Kg = entity.GetDataBlob <MassVolumeDB>().Mass; double ParentMass_kg = newtonMoveDB.ParentMass; var manager = entity.Manager; DateTime dateTimeFrom = newtonMoveDB.LastProcessDateTime; DateTime dateTimeNow = manager.ManagerSubpulses.StarSysDateTime; DateTime dateTimeFuture = dateTimeNow + TimeSpan.FromSeconds(deltaSeconds); double deltaT = (dateTimeFuture - dateTimeFrom).TotalSeconds; double secondsToItterate = deltaT; while (secondsToItterate > 0) { double speed_kms = newtonMoveDB.CurrentVector_kms.Length(); //double timeStep = Math.Max(secondsToItterate / speed_kms, 1); //timeStep = Math.Min(timeStep, secondsToItterate); double timeStep = 1;//because the above seems unstable and looses energy. double distanceToParent_m = Distance.AuToMt(positionDB.GetDistanceTo(newtonMoveDB.SOIParent.GetDataBlob <PositionDB>())); distanceToParent_m = Math.Max(distanceToParent_m, 0.1); //don't let the distance be 0 (once collision is in this will likely never happen anyway) double gravForce = GameConstants.Science.GravitationalConstant * (Mass_Kg * ParentMass_kg / Math.Pow(distanceToParent_m, 2)); Vector3 gravForceVector = gravForce * -Vector3.Normalise(positionDB.RelativePosition_AU); double distance = Distance.AuToKm(positionDB.RelativePosition_AU).Length(); Vector3 totalForce = gravForceVector + newtonMoveDB.ThrustVector; Vector3 acceleration_mps = totalForce / Mass_Kg; Vector3 newVelocity = (acceleration_mps * timeStep * 0.001) + newtonMoveDB.CurrentVector_kms; newtonMoveDB.CurrentVector_kms = newVelocity; Vector3 deltaPos = (newtonMoveDB.CurrentVector_kms + newVelocity) / 2 * timeStep; //Vector4 deltaPos = newtonMoveDB.CurrentVector_kms * timeStep; positionDB.RelativePosition_AU += Distance.KmToAU(deltaPos); double sOIRadius_AU = OrbitProcessor.GetSOI(newtonMoveDB.SOIParent); if (positionDB.RelativePosition_AU.Length() >= sOIRadius_AU) { Entity newParent; Vector3 parentRalitiveVector; //if our parent is a regular kepler object (normaly this is the case) if (newtonMoveDB.SOIParent.HasDataBlob <OrbitDB>()) { var orbitDB = newtonMoveDB.SOIParent.GetDataBlob <OrbitDB>(); newParent = orbitDB.Parent; var parentVelocity = OrbitProcessor.InstantaneousOrbitalVelocityVector(orbitDB, entity.Manager.ManagerSubpulses.StarSysDateTime); parentRalitiveVector = Distance.KmToAU(newtonMoveDB.CurrentVector_kms) + parentVelocity; var pvlen = Distance.AuToKm(parentVelocity.Length()); var vlen = newtonMoveDB.CurrentVector_kms.Length(); var rvlen = Distance.AuToKm(parentRalitiveVector.Length()); } else //if (newtonMoveDB.SOIParent.HasDataBlob<NewtonMoveDB>()) { //this will pretty much never happen. newParent = newtonMoveDB.SOIParent.GetDataBlob <NewtonMoveDB>().SOIParent; var parentVelocity = newtonMoveDB.SOIParent.GetDataBlob <NewtonMoveDB>().CurrentVector_kms; parentRalitiveVector = Distance.KmToAU(newtonMoveDB.CurrentVector_kms + parentVelocity); } double newParentMass = newParent.GetDataBlob <MassVolumeDB>().Mass; double sgp = GameConstants.Science.GravitationalConstant * (newParentMass + Mass_Kg) / 3.347928976e33; Vector3 posRalitiveToNewParent = positionDB.AbsolutePosition_AU - newParent.GetDataBlob <PositionDB>().AbsolutePosition_AU; var dateTime = dateTimeNow + TimeSpan.FromSeconds(deltaSeconds - secondsToItterate); var kE = OrbitMath.KeplerFromPositionAndVelocity(sgp, posRalitiveToNewParent, parentRalitiveVector, dateTime); if (kE.Eccentricity < 1) //if we're going to end up in a regular orbit around our new parent { /* * var newOrbit = OrbitDB.FromKeplerElements( * newParent, * newParentMass, * Mass_Kg, * kE, * dateTime); */ var newOrbit = OrbitDB.FromVector(newParent, entity, parentRalitiveVector, dateTime); entity.RemoveDataBlob <NewtonMoveDB>(); entity.SetDataBlob(newOrbit); positionDB.SetParent(newParent); var currentPos = Distance.AuToKm(positionDB.RelativePosition_AU); var newPos = OrbitProcessor.GetPosition_AU(newOrbit, dateTime); var newPosKM = Distance.AuToKm(newPos); positionDB.RelativePosition_AU = newPos; break; } else //else we're in a hyperbolic trajectory around our new parent, so just coninue the newtonion move. { positionDB.SetParent(newParent); newtonMoveDB.ParentMass = newParentMass; newtonMoveDB.SOIParent = newParent; } } secondsToItterate -= timeStep; } newtonMoveDB.LastProcessDateTime = dateTimeFuture; }
// sets the speed of the ship to the maximum speed allowed in the direction of the target. // Returns true if the destination has been reached or the target no longer exists. public override bool processOrder() { double speedMultiplier = 1.0; //was this just for debugging? if so we should remove it, if not, then maybe move it to game settings? double minimumDistance = 1000.0; PositionDB currentPosition = Owner.GetDataBlob <PositionDB>(); PositionDB targetPosition = null; double AUSpeed, kmSpeed; kmSpeed = Owner.GetDataBlob <PropulsionDB>().MaximumSpeed *speedMultiplier; AUSpeed = Distance.KmToAU(kmSpeed); currentPosition.SetParent(null); if (PositionTarget != null) { // just head straight towards the target position targetPosition = PositionTarget; // Assume that 1000 is extremely close, if (Distance.AuToKm(distanceBetweenPositions(currentPosition, targetPosition)) <= minimumDistance) { setPositionToTarget(Owner, targetPosition); Owner.GetDataBlob <PropulsionDB>().CurrentSpeed = new Vector4(0, 0, 0, 0); return(true); } else { Owner.GetDataBlob <PropulsionDB>().CurrentSpeed = getSpeed(currentPosition, targetPosition, kmSpeed); return(false); } } else if (Target == null || Target == Entity.InvalidEntity) // Target no longer exists { return(true); } else { targetPosition = Target.GetDataBlob <PositionDB>(); // Assume that 1000 is extremely close, if (Distance.AuToKm(distanceBetweenPositions(currentPosition, targetPosition)) <= minimumDistance) { setPositionToTarget(Owner, targetPosition); currentPosition.SetParent(Target); return(true); } if (Target.HasDataBlob <OrbitDB>()) { if (Target.GetDataBlob <OrbitDB>().IsStationary) { // just head straight towards the target position targetPosition = Target.GetDataBlob <PositionDB>(); Owner.GetDataBlob <PropulsionDB>().CurrentSpeed = getSpeed(currentPosition, targetPosition, kmSpeed); } else { // TODO: Figure out an intercept based on OrbitProcessor.GetPosition // for now, just head straight towards the target position targetPosition = Target.GetDataBlob <PositionDB>(); Owner.GetDataBlob <PropulsionDB>().CurrentSpeed = getSpeed(currentPosition, targetPosition, kmSpeed); return(false); } } else if (Target.HasDataBlob <PropulsionDB>()) { if (Target.GetDataBlob <PropulsionDB>().MaximumSpeed >= Owner.GetDataBlob <PropulsionDB>().MaximumSpeed) // Target is faster than our ship, and cannot intercept { // Just head in a straight line targetPosition = Target.GetDataBlob <PositionDB>(); Owner.GetDataBlob <PropulsionDB>().CurrentSpeed = getSpeed(currentPosition, targetPosition, kmSpeed); } else { // Calculate an intercept targetPosition = Target.GetDataBlob <PositionDB>(); Vector4 targetPos = new Vector4(targetPosition.X, targetPosition.Y, targetPosition.Z, 0); Vector4 currentPos = new Vector4(currentPosition.X, currentPosition.Y, currentPosition.Z, 0); targetPos = Find_collision_point(targetPos, Target.GetDataBlob <PropulsionDB>().CurrentSpeed, currentPos, AUSpeed); targetPosition = new PositionDB(targetPos, targetPosition.SystemGuid); Owner.GetDataBlob <PropulsionDB>().CurrentSpeed = getSpeed(currentPosition, targetPosition, kmSpeed); } } } return(false); }