public PartSim(Part thePart, int id, double atmosphere, LogMsg log) { part = thePart; partId = id; name = part.partInfo.name; if (log != null) log.buf.AppendLine("Create PartSim for " + name); parent = null; parentAttach = part.attachMode; fuelCrossFeed = part.fuelCrossFeed; noCrossFeedNodeKey = part.NoCrossFeedNodeKey; decoupledInStage = DecoupledInStage(part); isFuelLine = part is FuelLine; isFuelTank = part is FuelTank; isSepratron = IsSepratron(); inverseStage = part.inverseStage; //MonoBehaviour.print("inverseStage = " + inverseStage); cost = part.partInfo.cost; foreach (PartResource resource in part.Resources) { cost -= (float)((resource.maxAmount - resource.amount) * resource.info.unitCost); } // Work out if the part should have no physical significance isNoPhysics = part.HasModule<LaunchClamp>() || part.physicalSignificance == Part.PhysicalSignificance.NONE || part.PhysicsSignificance == 1; if (!isNoPhysics) baseMass = part.mass; if (SimManager.logOutput) MonoBehaviour.print((isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + part.mass); foreach (PartResource resource in part.Resources) { // Make sure it isn't NaN as this messes up the part mass and hence most of the values // This can happen if a resource capacity is 0 and tweakable if (!Double.IsNaN(resource.amount)) { if (SimManager.logOutput) MonoBehaviour.print(resource.resourceName + " = " + resource.amount); resources.Add(resource.info.id, resource.amount); resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0); } else { MonoBehaviour.print(resource.resourceName + " is NaN. Skipping."); } } startMass = GetMass(); hasVessel = (part.vessel != null); isLanded = hasVessel && part.vessel.Landed; if (hasVessel) { vesselName = part.vessel.vesselName; vesselType = part.vesselType; } initialVesselName = part.initialVesselName; hasMultiModeEngine = part.HasModule<MultiModeEngine>(); hasModuleEnginesFX = part.HasModule<ModuleEnginesFX>(); hasModuleEngines = part.HasModule<ModuleEngines>(); isEngine = hasMultiModeEngine || hasModuleEnginesFX || hasModuleEngines; if (SimManager.logOutput) MonoBehaviour.print("Created " + name + ". Decoupled in stage " + decoupledInStage); }
public EngineSim(PartSim theEngine, double atmosphere, double velocity, float maxThrust, float minThrust, float thrustPercentage, float requestedThrust, Vector3 vecThrust, float realIsp, FloatCurve atmosphereCurve, FloatCurve velocityCurve, bool throttleLocked, List<Propellant> propellants, bool active, bool correctThrust) { StringBuilder buffer = null; //MonoBehaviour.print("Create EngineSim for " + theEngine.name); //MonoBehaviour.print("maxThrust = " + maxThrust); //MonoBehaviour.print("minThrust = " + minThrust); //MonoBehaviour.print("thrustPercentage = " + thrustPercentage); //MonoBehaviour.print("requestedThrust = " + requestedThrust); //MonoBehaviour.print("velocity = " + velocity); partSim = theEngine; isActive = active; thrust = (maxThrust - minThrust) * (thrustPercentage / 100f) + minThrust; //MonoBehaviour.print("thrust = " + thrust); thrustVec = vecThrust; double flowRate = 0d; if (partSim.hasVessel) { //MonoBehaviour.print("hasVessel is true"); actualThrust = requestedThrust; if (velocityCurve != null) { actualThrust *= velocityCurve.Evaluate((float)velocity); //MonoBehaviour.print("actualThrust at velocity = " + actualThrust); } isp = atmosphereCurve.Evaluate((float)partSim.part.staticPressureAtm); if (isp == 0d) MonoBehaviour.print("Isp at " + partSim.part.staticPressureAtm + " is zero. Flow rate will be NaN"); if (correctThrust && realIsp == 0) { float ispsl = atmosphereCurve.Evaluate(0); if (ispsl != 0) { thrust = thrust * isp / ispsl; } else { MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust."); } //MonoBehaviour.print("corrected thrust = " + thrust); } if (velocityCurve != null) { thrust *= velocityCurve.Evaluate((float)velocity); //MonoBehaviour.print("thrust at velocity = " + thrust); } if (throttleLocked) { //MonoBehaviour.print("throttleLocked is true"); flowRate = thrust / (isp * 9.81d); } else { if (partSim.isLanded) { //MonoBehaviour.print("partSim.isLanded is true, mainThrottle = " + FlightInputHandler.state.mainThrottle); flowRate = Math.Max(0.000001d, thrust * FlightInputHandler.state.mainThrottle) / (isp * 9.81d); } else { if (requestedThrust > 0) { if (velocityCurve != null) { requestedThrust *= velocityCurve.Evaluate((float)velocity); //MonoBehaviour.print("requestedThrust at velocity = " + requestedThrust); } //MonoBehaviour.print("requestedThrust > 0"); flowRate = requestedThrust / (isp * 9.81d); } else { //MonoBehaviour.print("requestedThrust <= 0"); flowRate = thrust / (isp * 9.81d); } } } } else { //MonoBehaviour.print("hasVessel is false"); isp = atmosphereCurve.Evaluate((float)atmosphere); if (isp == 0d) MonoBehaviour.print("Isp at " + atmosphere + " is zero. Flow rate will be NaN"); if (correctThrust) { float ispsl = atmosphereCurve.Evaluate(0); if (ispsl != 0) { thrust = thrust * isp / ispsl; } else { MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust."); } //MonoBehaviour.print("corrected thrust = " + thrust); } if (velocityCurve != null) { thrust *= velocityCurve.Evaluate((float)velocity); //MonoBehaviour.print("thrust at velocity = " + thrust); } flowRate = thrust / (isp * 9.81d); } if (SimManager.logOutput) { buffer = new StringBuilder(1024); buffer.AppendFormat("flowRate = {0:g6}\n", flowRate); } float flowMass = 0f; foreach (Propellant propellant in propellants) flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); if (SimManager.logOutput) buffer.AppendFormat("flowMass = {0:g6}\n", flowMass); foreach (Propellant propellant in propellants) { if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") continue; double consumptionRate = propellant.ratio * flowRate / flowMass; if (SimManager.logOutput) buffer.AppendFormat("Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate); resourceConsumptions.Add(propellant.id, consumptionRate); } if (SimManager.logOutput) MonoBehaviour.print(buffer); }
public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log) { if (part.parent != null) { parent = null; if (partSimLookup.TryGetValue(part.parent, out parent)) { if (log != null) log.buf.AppendLine("Parent part is " + parent.name + ":" + parent.partId); } else { if (log != null) log.buf.AppendLine("No PartSim for parent part (" + part.parent.partInfo.name + ")"); } } }
private bool DrainFromSourceBeforeSelf(int type, PartSim source) { if (resources[type] < 1f) { return true; } if (source.part != this.part.parent) { return true; } if (this.part.parent == null) { return true; } foreach (AttachNode attachNode in this.part.parent.attachNodes) { if (attachNode.attachedPart == this.part && attachNode.nodeType != AttachNode.NodeType.Stack) { return false; } } return true; }
public void RemoveSourcePart(PartSim part) { if (sourceParts.Contains(part)) { sourceParts.Remove(part); } }
// This function prepares the simulation by creating all the necessary data structures it will // need during the simulation. All required data is copied from the core game data structures // so that the simulation itself can be run in a background thread without having issues with // the core game changing the data while the simulation is running. public bool PrepareSimulation(List<Part> parts, double theGravity, double theAtmosphere = 0, double theVelocity = 0, bool dumpTree = false, bool vectoredThrust = false) { LogMsg log = null; if (SimManager.logOutput) { log = new LogMsg(); log.buf.AppendLine("PrepareSimulation started"); dumpTree = true; } _timer.Start(); // Store the parameters in members for ease of access in other functions partList = parts; gravity = theGravity; atmosphere = theAtmosphere; velocity = theVelocity; lastStage = Staging.lastStage; //MonoBehaviour.print("lastStage = " + lastStage); // Create the lists for our simulation parts allParts = new List<PartSim>(); allFuelLines = new List<PartSim>(); drainingParts = new HashSet<PartSim>(); allEngines = new List<EngineSim>(); activeEngines = new List<EngineSim>(); drainingResources = new HashSet<int>(); // A dictionary for fast lookup of Part->PartSim during the preparation phase Dictionary<Part, PartSim> partSimLookup = new Dictionary<Part, PartSim>(); if (partList.Count > 0 && partList[0].vessel != null) { vesselName = partList[0].vessel.vesselName; vesselType = partList[0].vessel.vesselType; } // First we create a PartSim for each Part (giving each a unique id) int partId = 1; foreach (Part part in partList) { // If the part is already in the lookup dictionary then log it and skip to the next part if (partSimLookup.ContainsKey(part)) { if (log != null) log.buf.AppendLine("Part " + part.name + " appears in vessel list more than once"); continue; } // Create the PartSim PartSim partSim = new PartSim(part, partId, atmosphere, log); // Add it to the Part lookup dictionary and the necessary lists partSimLookup.Add(part, partSim); allParts.Add(partSim); if (partSim.isFuelLine) allFuelLines.Add(partSim); if (partSim.isEngine) partSim.CreateEngineSims(allEngines, atmosphere, velocity, vectoredThrust, log); partId++; } UpdateActiveEngines(); // Now that all the PartSims have been created we can do any set up that needs access to other parts // First we set up all the parent links foreach (PartSim partSim in allParts) { partSim.SetupParent(partSimLookup, log); } // Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets if (HighLogic.LoadedSceneIsEditor) { foreach (PartSim partSim in allFuelLines) { if ((partSim.part as FuelLine).target != null) { PartSim targetSim; if (partSimLookup.TryGetValue((partSim.part as FuelLine).target, out targetSim)) { if (log != null) log.buf.AppendLine("Fuel line target is " + targetSim.name + ":" + targetSim.partId); targetSim.fuelTargets.Add(partSim.parent); } else { if (log != null) log.buf.AppendLine("No PartSim for fuel line target (" + partSim.part.partInfo.name + ")"); } } else { if (log != null) log.buf.AppendLine("Fuel line target is null"); } } } //MonoBehaviour.print("SetupAttachNodes and count stages"); foreach (PartSim partSim in allParts) { partSim.SetupAttachNodes(partSimLookup, log); if (partSim.decoupledInStage >= lastStage) lastStage = partSim.decoupledInStage + 1; } // And finally release the Part references from all the PartSims //MonoBehaviour.print("ReleaseParts"); foreach (PartSim partSim in allParts) partSim.ReleasePart(); // And dereference the core's part list partList = null; _timer.Stop(); if (log != null) { log.buf.AppendLine("PrepareSimulation: " + _timer.ElapsedMilliseconds + "ms"); log.Flush(); } if (dumpTree) Dump(); return true; }
private void BuildVessel(List<Part> parts, double atmosphere) { partSims = new List<PartSim>(); Hashtable partSimLookup = new Hashtable(); foreach (Part part in parts) { PartSim partSim = new PartSim(part, atmosphere); if (partSim.decoupledInStage < currentStage) { partSim.SetResourceConsumptions(); partSims.Add(partSim); partSimLookup.Add(part, partSim); } } foreach (PartSim partSim in partSims) { partSim.SetSourceNodes(partSimLookup); } }
public AttachNodeSim(PartSim partSim, String newId, AttachNode.NodeType newNodeType) { attachedPartSim = partSim; nodeType = newNodeType; id = newId; }