// |
// |
// Kerbal Engineer Redux |
// Kerbal Engineer Redux |
// |
// |
// Copyright (C) 2014 CYBUTEK |
// Copyright (C) 2014 CYBUTEK |
// |
// |
// This program is free software: you can redistribute it and/or modify |
// This program is free software: you can redistribute it and/or modify |
// it under the terms of the GNU General Public License as published by |
// it under the terms of the GNU General Public License as published by |
// the Free Software Foundation, either version 3 of the License, or |
// the Free Software Foundation, either version 3 of the License, or |
// (at your option) any later version. |
// (at your option) any later version. |
// |
// |
// This program is distributed in the hope that it will be useful, |
// This program is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
// GNU General Public License for more details. |
// GNU General Public License for more details. |
// |
// |
// You should have received a copy of the GNU General Public License |
// You should have received a copy of the GNU General Public License |
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
// along with this program. If not, see <http://www.gnu.org/licenses/>. |
// |
// |
|
|
|
|
namespace KerbalEngineer.VesselSimulator |
namespace KerbalEngineer.VesselSimulator |
{ |
{ |
using System; |
using System; |
using System.Collections.Generic; |
using System.Collections.Generic; |
using System.Linq; |
using System.Linq; |
using System.Text; |
using System.Text; |
using CompoundParts; |
using CompoundParts; |
using Extensions; |
using Extensions; |
using UnityEngine; |
using UnityEngine; |
|
|
public class PartSim |
public class PartSim |
{ |
{ |
private static readonly Pool<PartSim> pool = new Pool<PartSim>(Create, Reset); |
private static readonly Pool<PartSim> pool = new Pool<PartSim>(Create, Reset); |
|
|
private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>(); |
private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>(); |
|
|
|
public double realMass; |
public double baseMass; |
public double baseMass; |
|
public double baseMassForCoM; |
public Vector3d centerOfMass; |
public Vector3d centerOfMass; |
public double cost; |
public double baseCost; |
public int decoupledInStage; |
public int decoupledInStage; |
public bool fuelCrossFeed; |
public bool fuelCrossFeed; |
public List<PartSim> fuelTargets = new List<PartSim>(); |
public List<PartSim> fuelTargets = new List<PartSim>(); |
|
public List<PartSim> surfaceMountFuelTargets = new List<PartSim>(); |
public bool hasModuleEngines; |
public bool hasModuleEngines; |
public bool hasModuleEnginesFX; |
|
public bool hasMultiModeEngine; |
public bool hasMultiModeEngine; |
|
|
public bool hasVessel; |
public bool hasVessel; |
public String initialVesselName; |
public String initialVesselName; |
public int inverseStage; |
public int inverseStage; |
public bool isDecoupler; |
public bool isDecoupler; |
public bool isEngine; |
public bool isEngine; |
public bool isFuelLine; |
public bool isFuelLine; |
public bool isFuelTank; |
public bool isFuelTank; |
public bool isLanded; |
public bool isLanded; |
public bool isNoPhysics; |
public bool isNoPhysics; |
public bool isSepratron; |
public bool isSepratron; |
public bool isFairing; |
public bool isFairing; |
public bool localCorrectThrust; |
public float fairingMass; |
public float moduleMass; |
|
public int stageIndex; |
public int stageIndex; |
public String name; |
public String name; |
public String noCrossFeedNodeKey; |
public String noCrossFeedNodeKey; |
public PartSim parent; |
public PartSim parent; |
public AttachModes parentAttach; |
public AttachModes parentAttach; |
public Part part; // This is only set while the data structures are being initialised |
public Part part; // This is only set while the data structures are being initialised |
public int partId = 0; |
public int partId = 0; |
public ResourceContainer resourceDrains = new ResourceContainer(); |
public ResourceContainer resourceDrains = new ResourceContainer(); |
public ResourceContainer resourceFlowStates = new ResourceContainer(); |
public ResourceContainer resourceFlowStates = new ResourceContainer(); |
public ResourceContainer resources = new ResourceContainer(); |
public ResourceContainer resources = new ResourceContainer(); |
public double startMass = 0d; |
public double startMass = 0d; |
public String vesselName; |
public String vesselName; |
public VesselType vesselType; |
public VesselType vesselType; |
|
|
|
|
private static PartSim Create() |
private static PartSim Create() |
{ |
{ |
return new PartSim(); |
return new PartSim(); |
} |
} |
|
|
private static void Reset(PartSim partSim) |
private static void Reset(PartSim partSim) |
{ |
{ |
for (int i = 0; i < partSim.attachNodes.Count; i++) |
for (int i = 0; i < partSim.attachNodes.Count; i++) |
{ |
{ |
partSim.attachNodes[i].Release(); |
partSim.attachNodes[i].Release(); |
} |
} |
partSim.attachNodes.Clear(); |
partSim.attachNodes.Clear(); |
partSim.fuelTargets.Clear(); |
partSim.fuelTargets.Clear(); |
partSim.resourceDrains.Reset(); |
partSim.resourceDrains.Reset(); |
partSim.resourceFlowStates.Reset(); |
partSim.resourceFlowStates.Reset(); |
partSim.resources.Reset(); |
partSim.resources.Reset(); |
|
partSim.baseCost = 0d; |
partSim.baseMass = 0d; |
partSim.baseMass = 0d; |
|
partSim.baseMassForCoM = 0d; |
partSim.startMass = 0d; |
partSim.startMass = 0d; |
} |
} |
|
|
public void Release() |
public void Release() |
{ |
{ |
pool.Release(this); |
pool.Release(this); |
} |
} |
|
|
public static PartSim New(Part thePart, int id, double atmosphere, LogMsg log) |
public static PartSim New(Part thePart, int id, double atmosphere, LogMsg log) |
{ |
{ |
PartSim partSim = pool.Borrow(); |
PartSim partSim = pool.Borrow(); |
|
|
|
|
partSim.part = thePart; |
partSim.part = thePart; |
partSim.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset); |
partSim.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset); |
partSim.partId = id; |
partSim.partId = id; |
partSim.name = partSim.part.partInfo.name; |
partSim.name = partSim.part.partInfo.name; |
|
|
if (log != null) |
if (log != null) log.buf.AppendLine("Create PartSim for " + partSim.name); |
{ |
|
log.buf.AppendLine("Create PartSim for " + partSim.name); |
|
} |
|
|
|
partSim.parent = null; |
partSim.parent = null; |
partSim.parentAttach = partSim.part.attachMode; |
partSim.parentAttach = partSim.part.attachMode; |
partSim.fuelCrossFeed = partSim.part.fuelCrossFeed; |
partSim.fuelCrossFeed = partSim.part.fuelCrossFeed; |
partSim.noCrossFeedNodeKey = partSim.part.NoCrossFeedNodeKey; |
partSim.noCrossFeedNodeKey = partSim.part.NoCrossFeedNodeKey; |
partSim.decoupledInStage = partSim.DecoupledInStage(partSim.part); |
partSim.decoupledInStage = partSim.DecoupledInStage(partSim.part); |
partSim.isFuelLine = partSim.part.HasModule<CModuleFuelLine>(); |
partSim.isFuelLine = partSim.part.HasModule<CModuleFuelLine>(); |
partSim.isFuelTank = partSim.part is FuelTank; |
partSim.isFuelTank = partSim.part is FuelTank; |
partSim.isSepratron = partSim.IsSepratron(); |
partSim.isSepratron = partSim.IsSepratron(); |
|
partSim.isFairing = partSim.IsFairing(partSim.part); |
partSim.inverseStage = partSim.part.inverseStage; |
partSim.inverseStage = partSim.part.inverseStage; |
//MonoBehaviour.print("inverseStage = " + inverseStage); |
//MonoBehaviour.print("inverseStage = " + inverseStage); |
|
|
partSim.cost = partSim.part.GetCostWet(); |
partSim.baseCost = partSim.part.GetCostDry(); |
|
|
|
if (log != null) |
|
{ |
|
log.buf.AppendLine("Parent part = " + (partSim.part.parent == null ? "null" : partSim.part.parent.partInfo.name)); |
|
log.buf.AppendLine("physicalSignificance = " + partSim.part.physicalSignificance); |
|
log.buf.AppendLine("PhysicsSignificance = " + partSim.part.PhysicsSignificance); |
|
} |
|
|
// Work out if the part should have no physical significance |
// Work out if the part should have no physical significance |
partSim.isNoPhysics = partSim.part.HasModule<LaunchClamp>() || |
// The root part is never "no physics" |
partSim.part.physicalSignificance == Part.PhysicalSignificance.NONE || |
partSim.isNoPhysics = partSim.part.physicalSignificance == Part.PhysicalSignificance.NONE || |
partSim.part.PhysicsSignificance == 1; |
partSim.part.PhysicsSignificance == 1; |
|
|
if (!partSim.isNoPhysics) |
if (partSim.part.HasModule<LaunchClamp>()) |
{ |
{ |
partSim.baseMass = partSim.part.mass; |
partSim.realMass = 0d; |
} |
if (log != null) log.buf.AppendLine("Ignoring mass of launch clamp"); |
|
} |
if (SimManager.logOutput) |
else |
{ |
{ |
MonoBehaviour.print((partSim.isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + partSim.part.mass); |
partSim.realMass = partSim.part.mass; |
|
if (log != null) log.buf.AppendLine("Using part.mass of " + partSim.part.mass); |
|
} |
|
|
|
if (partSim.isFairing) |
|
{ |
|
partSim.fairingMass = partSim.part.GetModuleMass((float)partSim.realMass); |
} |
} |
|
|
for (int i = 0; i < partSim.part.Resources.Count; i++) |
for (int i = 0; i < partSim.part.Resources.Count; i++) |
{ |
{ |
PartResource resource = partSim.part.Resources[i]; |
PartResource resource = partSim.part.Resources[i]; |
|
|
// Make sure it isn't NaN as this messes up the part mass and hence most of the values |
// 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 |
// This can happen if a resource capacity is 0 and tweakable |
if (!Double.IsNaN(resource.amount)) |
if (!Double.IsNaN(resource.amount)) |
{ |
{ |
if (SimManager.logOutput) |
if (log != null) |
{ |
log.buf.AppendLine(resource.resourceName + " = " + resource.amount); |
MonoBehaviour.print(resource.resourceName + " = " + resource.amount); |
|
} |
|
|
|
partSim.resources.Add(resource.info.id, resource.amount); |
partSim.resources.Add(resource.info.id, resource.amount); |
partSim.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0); |
partSim.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0); |
} |
} |
else |
else |
{ |
{ |
MonoBehaviour.print(resource.resourceName + " is NaN. Skipping."); |
if (log != null) log.buf.AppendLine(resource.resourceName + " is NaN. Skipping."); |
} |
} |
} |
} |
|
|
partSim.startMass = partSim.GetMass(-1); |
|
|
|
partSim.hasVessel = (partSim.part.vessel != null); |
partSim.hasVessel = (partSim.part.vessel != null); |
partSim.isLanded = partSim.hasVessel && partSim.part.vessel.Landed; |
partSim.isLanded = partSim.hasVessel && partSim.part.vessel.Landed; |
if (partSim.hasVessel) |
if (partSim.hasVessel) |
{ |
{ |
partSim.vesselName = partSim.part.vessel.vesselName; |
partSim.vesselName = partSim.part.vessel.vesselName; |
partSim.vesselType = partSim.part.vesselType; |
partSim.vesselType = partSim.part.vesselType; |
} |
} |
partSim.initialVesselName = partSim.part.initialVesselName; |
partSim.initialVesselName = partSim.part.initialVesselName; |
|
|
partSim.hasMultiModeEngine = partSim.part.HasModule<MultiModeEngine>(); |
partSim.hasMultiModeEngine = partSim.part.HasModule<MultiModeEngine>(); |
partSim.hasModuleEnginesFX = partSim.part.HasModule<ModuleEnginesFX>(); |
|
partSim.hasModuleEngines = partSim.part.HasModule<ModuleEngines>(); |
partSim.hasModuleEngines = partSim.part.HasModule<ModuleEngines>(); |
|
|
partSim.isEngine = partSim.hasMultiModeEngine || partSim.hasModuleEnginesFX || partSim.hasModuleEngines; |
partSim.isEngine = partSim.hasMultiModeEngine || partSim.hasModuleEngines; |
|
|
if (SimManager.logOutput) |
if (log != null) log.buf.AppendLine("Created " + partSim.name + ". Decoupled in stage " + partSim.decoupledInStage); |
{ |
|
MonoBehaviour.print("Created " + partSim.name + ". Decoupled in stage " + partSim.decoupledInStage); |
|
} |
|
return partSim; |
return partSim; |
} |
} |
|
|
public ResourceContainer ResourceDrains |
public ResourceContainer ResourceDrains |
{ |
{ |
get |
get |
{ |
{ |
return resourceDrains; |
return resourceDrains; |
} |
} |
} |
} |
|
|
public ResourceContainer Resources |
public ResourceContainer Resources |
{ |
{ |
get |
get |
{ |
{ |
return resources; |
return resources; |
} |
} |
} |
} |
|
|
public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log) |
public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log) |
{ |
{ |
bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(part); |
|
if (log != null) |
if (log != null) |
{ |
{ |
log.buf.AppendLine("CreateEngineSims for " + this.name); |
log.buf.AppendLine("CreateEngineSims for " + this.name); |
for (int i = 0; i < this.part.Modules.Count; i++) |
for (int i = 0; i < this.part.Modules.Count; i++) |
{ |
{ |
PartModule partMod = this.part.Modules[i]; |
PartModule partMod = this.part.Modules[i]; |
log.buf.AppendLine("Module: " + partMod.moduleName); |
log.buf.AppendLine("Module: " + partMod.moduleName); |
} |
} |
|
|
log.buf.AppendLine("correctThrust = " + correctThrust); |
|
} |
} |
|
|
if (hasMultiModeEngine) |
if (hasMultiModeEngine) |
{ |
{ |
// A multi-mode engine has multiple ModuleEnginesFX but only one is active at any point |
// A multi-mode engine has multiple ModuleEngines but only one is active at any point |
// The mode of the engine is the engineID of the ModuleEnginesFX that is active |
// The mode of the engine is the engineID of the ModuleEngines that is active |
string mode = part.GetModule<MultiModeEngine>().mode; |
string mode = part.GetModule<MultiModeEngine>().mode; |
|
|
List<ModuleEnginesFX> engines = part.GetModules<ModuleEnginesFX>(); |
List<ModuleEngines> engines = part.GetModules<ModuleEngines>(); |
for (int i = 0; i < engines.Count; ++i) |
for (int i = 0; i < engines.Count; ++i) |
{ |
{ |
ModuleEnginesFX engine = engines[i]; |
ModuleEngines engine = engines[i]; |
if (engine.engineID == mode) |
if (engine.engineID == mode) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("Module: " + engine.moduleName); |
{ |
|
log.buf.AppendLine("Module: " + engine.moduleName); |
|
} |
|
|
|
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); |
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); |
|
|
EngineSim engineSim = EngineSim.New( |
EngineSim engineSim = EngineSim.New( |
this, |
this, |
atmosphere, |
atmosphere, |
(float)mach, |
(float)mach, |
engine.maxFuelFlow, |
engine.maxFuelFlow, |
engine.minFuelFlow, |
engine.minFuelFlow, |
engine.thrustPercentage, |
engine.thrustPercentage, |
thrustvec, |
thrustvec, |
engine.atmosphereCurve, |
engine.atmosphereCurve, |
engine.atmChangeFlow, |
engine.atmChangeFlow, |
engine.useAtmCurve ? engine.atmCurve : null, |
engine.useAtmCurve ? engine.atmCurve : null, |
engine.useVelCurve ? engine.velCurve : null, |
engine.useVelCurve ? engine.velCurve : null, |
engine.currentThrottle, |
engine.currentThrottle, |
engine.g, |
engine.g, |
engine.throttleLocked || fullThrust, |
engine.throttleLocked || fullThrust, |
engine.propellants, |
engine.propellants, |
engine.isOperational, |
engine.isOperational, |
engine.resultingThrust, |
engine.resultingThrust, |
engine.thrustTransforms); |
engine.thrustTransforms, |
|
log); |
allEngines.Add(engineSim); |
allEngines.Add(engineSim); |
} |
} |
} |
} |
} |
} |
else |
else if (hasModuleEngines) |
{ |
{ |
if (hasModuleEngines) |
List<ModuleEngines> engines = part.GetModules<ModuleEngines>(); |
{ |
for (int i = 0; i < engines.Count; ++i) |
List<ModuleEngines> engines = part.GetModules<ModuleEngines>(); |
{ |
for (int i = 0; i < engines.Count; ++i) |
ModuleEngines engine = engines[i]; |
{ |
if (log != null) log.buf.AppendLine("Module: " + engine.moduleName); |
ModuleEngines engine = engines[i]; |
|
if (log != null) |
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); |
{ |
|
log.buf.AppendLine("Module: " + engine.moduleName); |
EngineSim engineSim = EngineSim.New( |
} |
this, |
|
atmosphere, |
Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); |
(float)mach, |
|
engine.maxFuelFlow, |
EngineSim engineSim = EngineSim.New( |
engine.minFuelFlow, |
this, |
engine.thrustPercentage, |
atmosphere, |
thrustvec, |
(float)mach, |
engine.atmosphereCurve, |
engine.maxFuelFlow, |
engine.atmChangeFlow, |
engine.minFuelFlow, |
engine.useAtmCurve ? engine.atmCurve : null, |
engine.thrustPercentage, |
engine.useVelCurve ? engine.velCurve : null, |
thrustvec, |
engine.currentThrottle, |
engine.atmosphereCurve, |
engine.g, |
engine.atmChangeFlow, |
engine.throttleLocked || fullThrust, |
engine.useAtmCurve ? engine.atmCurve : null, |
engine.propellants, |
engine.useVelCurve ? engine.velCurve : null, |
engine.isOperational, |
engine.currentThrottle, |
engine.resultingThrust, |
engine.g, |
engine.thrustTransforms, |
engine.throttleLocked || fullThrust, |
log); |
engine.propellants, |
allEngines.Add(engineSim); |
engine.isOperational, |
|
engine.resultingThrust, |
|
engine.thrustTransforms); |
|
allEngines.Add(engineSim); |
|
} |
|
} |
} |
} |
} |
|
|
if (log != null) |
if (log != null) |
{ |
{ |
log.Flush(); |
log.Flush(); |
} |
} |
} |
} |
|
|
public int DecouplerCount() |
public int DecouplerCount() |
{ |
{ |
int count = 0; |
int count = 0; |
PartSim partSim = this; |
PartSim partSim = this; |
while (partSim != null) |
while (partSim != null) |
{ |
{ |
if (partSim.isDecoupler) |
if (partSim.isDecoupler) |
{ |
{ |
count++; |
count++; |
} |
} |
|
|
partSim = partSim.parent; |
partSim = partSim.parent; |
} |
} |
return count; |
return count; |
} |
} |
|
|
public void DrainResources(double time) |
public void DrainResources(double time) |
{ |
{ |
//MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")"); |
//MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")"); |
for (int i = 0; i < resourceDrains.Types.Count; ++i) |
for (int i = 0; i < resourceDrains.Types.Count; ++i) |
{ |
{ |
int type = resourceDrains.Types[i]; |
int type = resourceDrains.Types[i]; |
|
|
//MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type)); |
//MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type)); |
resources.Add(type, -time * resourceDrains[type]); |
resources.Add(type, -time * resourceDrains[type]); |
//MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]); |
//MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]); |
} |
} |
} |
} |
|
|
public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix) |
public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix) |
{ |
{ |
if (parent != null) |
if (parent != null) |
{ |
{ |
prefix = parent.DumpPartAndParentsToBuffer(buffer, prefix) + " "; |
prefix = parent.DumpPartAndParentsToBuffer(buffer, prefix) + " "; |
} |
} |
|
|
DumpPartToBuffer(buffer, prefix); |
DumpPartToBuffer(buffer, prefix); |
|
|
return prefix; |
return prefix; |
} |
} |
|
|
public void DumpPartToBuffer(StringBuilder buffer, String prefix, List<PartSim> allParts = null) |
public void DumpPartToBuffer(StringBuilder buffer, String prefix, List<PartSim> allParts = null) |
{ |
{ |
buffer.Append(prefix); |
buffer.Append(prefix); |
buffer.Append(name); |
buffer.Append(name); |
buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", partId, decoupledInStage, inverseStage); |
buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", partId, decoupledInStage, inverseStage); |
|
|
buffer.AppendFormat(", vesselName = '{0}'", vesselName); |
//buffer.AppendFormat(", vesselName = '{0}'", vesselName); |
buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(vesselType)); |
//buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(vesselType)); |
buffer.AppendFormat(", initialVesselName = '{0}'", initialVesselName); |
//buffer.AppendFormat(", initialVesselName = '{0}'", initialVesselName); |
|
|
|
buffer.AppendFormat(", isNoPhys = {0}", isNoPhysics); |
|
buffer.AppendFormat(", baseMass = {0}", baseMass); |
|
buffer.AppendFormat(", baseMassForCoM = {0}", baseMassForCoM); |
|
|
buffer.AppendFormat(", fuelCF = {0}", fuelCrossFeed); |
buffer.AppendFormat(", fuelCF = {0}", fuelCrossFeed); |
buffer.AppendFormat(", noCFNKey = '{0}'", noCrossFeedNodeKey); |
buffer.AppendFormat(", noCFNKey = '{0}'", noCrossFeedNodeKey); |
|
|
buffer.AppendFormat(", isSep = {0}", isSepratron); |
buffer.AppendFormat(", isSep = {0}", isSepratron); |
|
|
foreach (int type in resources.Types) |
for (int i = 0; i < resources.Types.Count; i++) |
{ |
{ |
|
int type = resources.Types[i]; |
buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]); |
buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]); |
} |
} |
|
|
if (attachNodes.Count > 0) |
if (attachNodes.Count > 0) |
{ |
{ |
buffer.Append(", attached = <"); |
buffer.Append(", attached = <"); |
attachNodes[0].DumpToBuffer(buffer); |
attachNodes[0].DumpToBuffer(buffer); |
for (int i = 1; i < attachNodes.Count; i++) |
for (int i = 1; i < attachNodes.Count; i++) |
{ |
{ |
buffer.Append(", "); |
buffer.Append(", "); |
attachNodes[i].DumpToBuffer(buffer); |
attachNodes[i].DumpToBuffer(buffer); |
} |
} |
buffer.Append(">"); |
buffer.Append(">"); |
} |
} |
|
|
// Add more info here |
// Add more info here |
|
|
buffer.Append("]\n"); |
buffer.Append("]\n"); |
|
|
if (allParts != null) |
if (allParts != null) |
{ |
{ |
String newPrefix = prefix + " "; |
String newPrefix = prefix + " "; |
foreach (PartSim partSim in allParts) |
for (int i = 0; i < allParts.Count; i++) |
{ |
{ |
|
PartSim partSim = allParts[i]; |
if (partSim.parent == this) |
if (partSim.parent == this) |
{ |
|
partSim.DumpPartToBuffer(buffer, newPrefix, allParts); |
partSim.DumpPartToBuffer(buffer, newPrefix, allParts); |
} |
|
} |
} |
} |
} |
} |
} |
|
|
public bool EmptyOf(HashSet<int> types) |
public bool EmptyOf(HashSet<int> types) |
{ |
{ |
foreach (int type in types) |
foreach (int type in types) |
{ |
{ |
if (resources.HasType(type) && resourceFlowStates[type] != 0 && resources[type] > SimManager.RESOURCE_PART_EMPTY_THRESH) |
if (resources.HasType(type) && resourceFlowStates[type] != 0 && resources[type] > SimManager.RESOURCE_PART_EMPTY_THRESH) |
{ |
|
return false; |
return false; |
} |
|
} |
} |
|
|
return true; |
return true; |
} |
} |
|
|
public double GetMass(int currentStage) |
public double GetMass(int currentStage, bool forCoM = false) |
{ |
{ |
double mass = baseMass; |
if (decoupledInStage >= currentStage) |
|
return 0d; |
|
|
|
double mass = forCoM ? baseMassForCoM : baseMass; |
|
|
for (int i = 0; i < resources.Types.Count; ++i) |
for (int i = 0; i < resources.Types.Count; ++i) |
{ |
{ |
mass += resources.GetResourceMass(resources.Types[i]); |
mass += resources.GetResourceMass(resources.Types[i]); |
} |
} |
|
|
if (hasVessel == false && isFairing && inverseStage < currentStage) |
if (isFairing && currentStage <= inverseStage) |
{ |
{ |
mass = mass + moduleMass; |
mass -= fairingMass; |
} |
} |
|
|
return mass; |
return mass; |
} |
} |
|
|
|
public double GetCost(int currentStage) |
|
{ |
|
if (decoupledInStage >= currentStage) |
|
return 0d; |
|
|
|
double cost = baseCost; |
|
|
|
for (int i = 0; i < resources.Types.Count; ++i) |
|
{ |
|
cost += resources.GetResourceCost(resources.Types[i]); |
|
} |
|
|
|
return cost; |
|
} |
|
|
public void ReleasePart() |
public void ReleasePart() |
{ |
{ |
this.part = null; |
this.part = null; |
} |
} |
|
|
// All functions below this point must not rely on the part member (it may be null) |
// All functions below this point must not rely on the part member (it may be null) |
// |
// |
|
|
public void GetSourceSet(int type, List<PartSim> allParts, HashSet<PartSim> visited, HashSet<PartSim> allSources, LogMsg log, String indent) |
public void GetSourceSet(int type, bool includeSurfaceMountedParts, List<PartSim> allParts, HashSet<PartSim> visited, HashSet<PartSim> allSources, LogMsg log, String indent) |
{ |
{ |
if (log != null) |
if (log != null) |
{ |
{ |
log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + name + ":" + partId); |
log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + name + ":" + partId); |
indent += " "; |
indent += " "; |
} |
} |
|
|
// Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is. |
// Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is. |
if (visited.Contains(this)) |
if (visited.Contains(this)) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Returning empty set, already visited (" + name + ":" + partId + ")"); |
log.buf.AppendLine(indent + "Returning empty set, already visited (" + name + ":" + partId + ")"); |
|
|
|
return; |
return; |
} |
} |
|
|
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Adding this to visited"); |
log.buf.AppendLine(indent + "Adding this to visited"); |
|
|
|
visited.Add(this); |
visited.Add(this); |
|
|
// Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed. |
// Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed. |
// Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result. |
// Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result. |
//MonoBehaviour.print("foreach fuel line"); |
//MonoBehaviour.print("for each fuel line"); |
|
|
int lastCount = allSources.Count; |
int lastCount = allSources.Count; |
|
|
for (int i = 0; i < this.fuelTargets.Count; i++) |
for (int i = 0; i < this.fuelTargets.Count; i++) |
{ |
{ |
PartSim partSim = this.fuelTargets[i]; |
PartSim partSim = this.fuelTargets[i]; |
if (partSim != null) |
if (partSim != null) |
{ |
{ |
if (visited.Contains(partSim)) |
if (visited.Contains(partSim)) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")"); |
log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")"); |
|
} |
} |
else |
else |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")"); |
log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")"); |
|
|
partSim.GetSourceSet(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent); |
partSim.GetSourceSet(type, allParts, visited, allSources, log, indent); |
} |
|
} |
|
} |
|
|
|
// check surface mounted fuel targets |
|
if (includeSurfaceMountedParts) |
|
{ |
|
for (int i = 0; i < surfaceMountFuelTargets.Count; i++) |
|
{ |
|
PartSim partSim = this.surfaceMountFuelTargets[i]; |
|
if (partSim != null) |
|
{ |
|
if (visited.Contains(partSim)) |
|
{ |
|
if (log != null) log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")"); |
|
} |
|
else |
|
{ |
|
if (log != null) log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")"); |
|
|
|
partSim.GetSourceSet(type, true, allParts, visited, allSources, log, indent); |
|
} |
} |
} |
} |
} |
} |
} |
|
|
if (allSources.Count > lastCount) |
if (allSources.Count > lastCount) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " fuel target sources (" + this.name + ":" + this.partId + ")"); |
log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " fuel target sources (" + this.name + ":" + this.partId + ")"); |
|
|
|
return; |
return; |
} |
} |
|
|
|
|
// Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed |
// Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed |
|
|
// Rule 4: Part performs scan on each of its axially mounted neighbors. |
// Rule 4: Part performs scan on each of its axially mounted neighbors. |
// Couplers (bicoupler, tricoupler, ...) are an exception, they only scan one attach point on the single attachment side, |
// Couplers (bicoupler, tricoupler, ...) are an exception, they only scan one attach point on the single attachment side, |
// skip the points on the side where multiple points are. [Experiment] |
// skip the points on the side where multiple points are. [Experiment] |
// Again, the part creates union of scan lists from each of its neighbor and if it is not empty, returns this list. |
// Again, the part creates union of scan lists from each of its neighbor and if it is not empty, returns this list. |
// The order in which mount points of a part are scanned appears to be fixed and defined by the part specification file. [Experiment] |
// The order in which mount points of a part are scanned appears to be fixed and defined by the part specification file. [Experiment] |
if (fuelCrossFeed) |
if (fuelCrossFeed) |
{ |
{ |
lastCount = allSources.Count; |
lastCount = allSources.Count; |
//MonoBehaviour.print("foreach attach node"); |
//MonoBehaviour.print("for each attach node"); |
for (int i = 0; i < this.attachNodes.Count; i++) |
for (int i = 0; i < this.attachNodes.Count; i++) |
{ |
{ |
AttachNodeSim attachSim = this.attachNodes[i]; |
AttachNodeSim attachSim = this.attachNodes[i]; |
if (attachSim.attachedPartSim != null) |
if (attachSim.attachedPartSim != null) |
{ |
{ |
if (attachSim.nodeType == AttachNode.NodeType.Stack) |
if (attachSim.nodeType == AttachNode.NodeType.Stack) |
{ |
{ |
if ( |
if ((string.IsNullOrEmpty(noCrossFeedNodeKey) == false && attachSim.id.Contains(noCrossFeedNodeKey)) == false) |
!(this.noCrossFeedNodeKey != null && this.noCrossFeedNodeKey.Length > 0 && |
|
attachSim.id.Contains(this.noCrossFeedNodeKey))) |
|
{ |
{ |
if (visited.Contains(attachSim.attachedPartSim)) |
if (visited.Contains(attachSim.attachedPartSim)) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Attached part already visited, skipping (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")"); |
log.buf.AppendLine(indent + "Attached part already visited, skipping (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")"); |
|
} |
} |
else |
else |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Adding attached part as source (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")"); |
log.buf.AppendLine(indent + "Adding attached part as source (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")"); |
|
|
attachSim.attachedPartSim.GetSourceSet(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent); |
attachSim.attachedPartSim.GetSourceSet(type, allParts, visited, allSources, log, indent); |
|
} |
} |
} |
} |
} |
} |
} |
} |
} |
} |
|
|
if (allSources.Count > lastCount) |
if (allSources.Count > lastCount) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " attached sources (" + this.name + ":" + this.partId + ")"); |
log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " attached sources (" + this.name + ":" + this.partId + ")"); |
|
|
|
return; |
return; |
} |
} |
} |
} |
|
|
// Rule 5: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel |
// Rule 5: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel |
// type was not disabled [Experiment]) and it contains fuel, it returns itself. |
// type was not disabled [Experiment]) and it contains fuel, it returns itself. |
// Rule 6: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel |
// Rule 6: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel |
// type was not disabled) but it does not contain the requested fuel, it returns empty list. [Experiment] |
// type was not disabled) but it does not contain the requested fuel, it returns empty list. [Experiment] |
if (resources.HasType(type) && resourceFlowStates[type] != 0) |
if (resources.HasType(type) && resourceFlowStates[type] > 0.0) |
{ |
{ |
if (resources[type] > SimManager.RESOURCE_MIN) |
if (resources[type] > SimManager.RESOURCE_MIN) |
{ |
{ |
allSources.Add(this); |
allSources.Add(this); |
|
|
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Returning enabled tank as only source (" + name + ":" + partId + ")"); |
log.buf.AppendLine(indent + "Returning enabled tank as only source (" + name + ":" + partId + ")"); |
|
} |
} |
|
|
return; |
return; |
} |
} |
else |
else |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Not fuel tank or disabled. HasType = " + resources.HasType(type) + " FlowState = " + resourceFlowStates[type]); |
log.buf.AppendLine(indent + "Not fuel tank or disabled. HasType = " + resources.HasType(type) + " FlowState = " + resourceFlowStates[type]); |
|
} |
} |
|
|
// Rule 7: If the part is radially attached to another part and it is child of that part in the ship's tree structure, it scans its |
// Rule 7: If the part is radially attached to another part and it is child of that part in the ship's tree structure, it scans its |
// parent and returns whatever the parent scan returned. [Experiment] [Experiment] |
// parent and returns whatever the parent scan returned. [Experiment] [Experiment] |
if (parent != null && parentAttach == AttachModes.SRF_ATTACH) |
if (parent != null && parentAttach == AttachModes.SRF_ATTACH) |
{ |
{ |
if (fuelCrossFeed) |
if (fuelCrossFeed) |
{ |
{ |
if (visited.Contains(parent)) |
if (visited.Contains(parent)) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Parent part already visited, skipping (" + parent.name + ":" + parent.partId + ")"); |
log.buf.AppendLine(indent + "Parent part already visited, skipping (" + parent.name + ":" + parent.partId + ")"); |
|
} |
} |
else |
else |
{ |
{ |
lastCount = allSources.Count; |
lastCount = allSources.Count; |
this.parent.GetSourceSet(type, allParts, visited, allSources, log, indent); |
this.parent.GetSourceSet(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent); |
if (allSources.Count > lastCount) |
if (allSources.Count > lastCount) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " parent sources (" + this.name + ":" + this.partId + ")"); |
log.buf.AppendLine(indent + "Returning " + (allSources.Count - lastCount) + " parent sources (" + this.name + ":" + this.partId + ")"); |
|
|
|
return; |
return; |
} |
} |
} |
} |
} |
} |
} |
} |
|
|
// Rule 8: If all preceding rules failed, part returns empty list. |
// Rule 8: If all preceding rules failed, part returns empty list. |
if (log != null) |
if (log != null) log.buf.AppendLine(indent + "Returning empty set, no sources found (" + name + ":" + partId + ")"); |
log.buf.AppendLine(indent + "Returning empty set, no sources found (" + name + ":" + partId + ")"); |
|
|
|
return; |
return; |
} |
} |
|
|
public double GetStartMass() |
public double GetStartMass() |
{ |
{ |
return startMass; |
return startMass; |
} |
} |
|
|
public void RemoveAttachedParts(HashSet<PartSim> partSims) |
public void RemoveAttachedParts(HashSet<PartSim> partSims) |
{ |
{ |
// Loop through the attached parts |
// Loop through the attached parts |
for (int i = 0; i < this.attachNodes.Count; i++) |
for (int i = 0; i < this.attachNodes.Count; i++) |
{ |
{ |
AttachNodeSim attachSim = this.attachNodes[i]; |
AttachNodeSim attachSim = this.attachNodes[i]; |
// If the part is in the set then "remove" it by clearing the PartSim reference |
// If the part is in the set then "remove" it by clearing the PartSim reference |
if (partSims.Contains(attachSim.attachedPartSim)) |
if (partSims.Contains(attachSim.attachedPartSim)) |
{ |
{ |
attachSim.attachedPartSim = null; |
attachSim.attachedPartSim = null; |
} |
} |
} |
} |
|
|
// Loop through the fuel targets (fuel line sources) |
// Loop through the fuel targets (fuel line sources) |
for (int i = 0; i < this.fuelTargets.Count; i++) |
for (int i = 0; i < this.fuelTargets.Count; i++) |
{ |
{ |
PartSim fuelTargetSim = this.fuelTargets[i]; |
PartSim fuelTargetSim = this.fuelTargets[i]; |
// If the part is in the set then "remove" it by clearing the PartSim reference |
// If the part is in the set then "remove" it by clearing the PartSim reference |
if (fuelTargetSim != null && partSims.Contains(fuelTargetSim)) |
if (fuelTargetSim != null && partSims.Contains(fuelTargetSim)) |
{ |
{ |
this.fuelTargets[i] = null; |
this.fuelTargets[i] = null; |
} |
} |
} |
} |
} |
} |
|
|
public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log) |
public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("SetupAttachNodes for " + name + ":" + partId + ""); |
{ |
|
log.buf.AppendLine("SetupAttachNodes for " + name + ":" + partId + ""); |
|
} |
|
|
|
attachNodes.Clear(); |
attachNodes.Clear(); |
|
|
for (int i = 0; i < part.attachNodes.Count; ++i) |
for (int i = 0; i < part.attachNodes.Count; ++i) |
{ |
{ |
AttachNode attachNode = part.attachNodes[i]; |
AttachNode attachNode = part.attachNodes[i]; |
|
|
if (log != null) |
if (log != null) log.buf.AppendLine("AttachNode " + attachNode.id + " = " + (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null")); |
{ |
|
log.buf.AppendLine("AttachNode " + attachNode.id + " = " + (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null")); |
|
} |
|
|
|
if (attachNode.attachedPart != null && attachNode.id != "Strut") |
if (attachNode.attachedPart != null && attachNode.id != "Strut") |
{ |
{ |
PartSim attachedSim; |
PartSim attachedSim; |
if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim)) |
if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim)) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("Adding attached node " + attachedSim.name + ":" + attachedSim.partId + ""); |
{ |
|
log.buf.AppendLine("Adding attached node " + attachedSim.name + ":" + attachedSim.partId + ""); |
|
} |
|
|
|
attachNodes.Add(AttachNodeSim.New(attachedSim, attachNode.id, attachNode.nodeType)); |
attachNodes.Add(AttachNodeSim.New(attachedSim, attachNode.id, attachNode.nodeType)); |
} |
} |
else |
else |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")"); |
{ |
|
log.buf.AppendLine("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")"); |
|
} |
|
} |
} |
} |
} |
} |
} |
|
|
for (int i = 0; i < part.fuelLookupTargets.Count; ++i) |
for (int i = 0; i < part.fuelLookupTargets.Count; ++i) |
{ |
{ |
Part p = part.fuelLookupTargets[i]; |
Part p = part.fuelLookupTargets[i]; |
|
|
if (p != null) |
if (p != null) |
{ |
{ |
PartSim targetSim; |
PartSim targetSim; |
if (partSimLookup.TryGetValue(p, out targetSim)) |
if (partSimLookup.TryGetValue(p, out targetSim)) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("Fuel target: " + targetSim.name + ":" + targetSim.partId); |
{ |
|
log.buf.AppendLine("Fuel target: " + targetSim.name + ":" + targetSim.partId); |
|
} |
|
|
|
fuelTargets.Add(targetSim); |
fuelTargets.Add(targetSim); |
} |
} |
else |
else |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("No PartSim for fuel target (" + p.name + ")"); |
{ |
|
log.buf.AppendLine("No PartSim for fuel target (" + p.name + ")"); |
|
} |
|
} |
} |
} |
} |
} |
} |
} |
} |
|
|
public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log) |
public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log) |
{ |
{ |
if (part.parent != null) |
if (part.parent != null) |
{ |
{ |
parent = null; |
parent = null; |
if (partSimLookup.TryGetValue(part.parent, out parent)) |
if (partSimLookup.TryGetValue(part.parent, out parent)) |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("Parent part is " + parent.name + ":" + parent.partId); |
{ |
if (part.attachMode == AttachModes.SRF_ATTACH && part.attachRules.srfAttach && part.fuelCrossFeed && part.parent.fuelCrossFeed) |
log.buf.AppendLine("Parent part is " + parent.name + ":" + parent.partId); |
{ |
|
if (log != null) log.buf.AppendLine("Added " + name + " to " + parent.name + " surface mounted fuel targets."); |
|
parent.surfaceMountFuelTargets.Add(this); |
} |
} |
} |
} |
else |
else |
{ |
{ |
if (log != null) |
if (log != null) log.buf.AppendLine("No PartSim for parent part (" + part.parent.partInfo.name + ")"); |
{ |
|
log.buf.AppendLine("No PartSim for parent part (" + part.parent.partInfo.name + ")"); |
|
} |
|
} |
} |
} |
} |
} |
} |
|
|
public double TimeToDrainResource() |
public double TimeToDrainResource() |
{ |
{ |
//MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")"); |
//MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")"); |
double time = double.MaxValue; |
double time = double.MaxValue; |
|
|
for (int i = 0; i < resourceDrains.Types.Count; ++i) |
for (int i = 0; i < resourceDrains.Types.Count; ++i) |
{ |
{ |
int type = resourceDrains.Types[i]; |
int type = resourceDrains.Types[i]; |
|
|
if (resourceDrains[type] > 0) |
if (resourceDrains[type] > 0) |
{ |
{ |
time = Math.Min(time, resources[type] / resourceDrains[type]); |
time = Math.Min(time, resources[type] / resourceDrains[type]); |
//MonoBehaviour.print("type = " + ResourceContainer.GetResourceName(type) + " amount = " + resources[type] + " rate = " + resourceDrains[type] + " time = " + time); |
//MonoBehaviour.print("type = " + ResourceContainer.GetResourceName(type) + " amount = " + resources[type] + " rate = " + resourceDrains[type] + " time = " + time); |
} |
} |
} |
} |
|
|
//if (time < double.MaxValue) |
//if (time < double.MaxValue) |
// MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ") = " + time); |
// MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ") = " + time); |
return time; |
return time; |
} |
} |
|
|
private Vector3 CalculateThrustVector(List<Transform> thrustTransforms, LogMsg log) |
private Vector3 CalculateThrustVector(List<Transform> thrustTransforms, LogMsg log) |
{ |
{ |
if (thrustTransforms == null) |
if (thrustTransforms == null) |
{ |
{ |
return Vector3.forward; |
return Vector3.forward; |
} |
} |
|
|
Vector3 thrustvec = Vector3.zero; |
Vector3 thrustvec = Vector3.zero; |
for (int i = 0; i < thrustTransforms.Count; ++i) |
for (int i = 0; i < thrustTransforms.Count; ++i) |
{ |
{ |
Transform trans = thrustTransforms[i]; |
Transform trans = thrustTransforms[i]; |
|
|
if (log != null) |
if (log != null) log.buf.AppendFormat("Transform = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", trans.forward.x, trans.forward.y, trans.forward.z, trans.forward.magnitude); |
{ |
|
log.buf.AppendFormat("Transform = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", trans.forward.x, trans.forward.y, trans.forward.z, trans.forward.magnitude); |
|
} |
|
|
|
thrustvec -= trans.forward; |
thrustvec -= trans.forward; |
} |
} |
|
|
if (log != null) |
if (log != null) log.buf.AppendFormat("ThrustVec = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); |
{ |
|
log.buf.AppendFormat("ThrustVec = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); |
|
} |
|
|
|
thrustvec.Normalize(); |
thrustvec.Normalize(); |
|
|
if (log != null) |
if (log != null) log.buf.AppendFormat("ThrustVecN = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); |
{ |
|
log.buf.AppendFormat("ThrustVecN = ({0:g6}, {1:g6}, {2:g6}) length = {3:g6}\n", thrustvec.x, thrustvec.y, thrustvec.z, thrustvec.magnitude); |
|
} |
|
|
|
return thrustvec; |
return thrustvec; |
} |
} |
|
|
private int DecoupledInStage(Part thePart, int stage = -1) |
private int DecoupledInStage(Part thePart, int stage = -1) |
{ |
{ |
if (IsDecoupler(thePart)) |
if (IsDecoupler(thePart) && thePart.inverseStage > stage) |
{ |
stage = thePart.inverseStage; |
if (thePart.inverseStage > stage) |
|
{ |
|
stage = thePart.inverseStage; |
|
} |
|
} |
|
|
|
if (thePart.parent != null) |
if (thePart.parent != null) |
{ |
|
stage = DecoupledInStage(thePart.parent, stage); |
stage = DecoupledInStage(thePart.parent, stage); |
} |
|
|
|
return stage; |
return stage; |
} |
} |
|
|
private bool IsActiveDecoupler(Part thePart) |
private bool IsActiveDecoupler(Part thePart) |
{ |
{ |
return thePart.FindModulesImplementing<ModuleDecouple>().Any(mod => !mod.isDecoupled) || |
return thePart.FindModulesImplementing<ModuleDecouple>().Any(mod => !mod.isDecoupled) || |
thePart.FindModulesImplementing<ModuleAnchoredDecoupler>().Any(mod => !mod.isDecoupled); |
thePart.FindModulesImplementing<ModuleAnchoredDecoupler>().Any(mod => !mod.isDecoupled); |
} |
} |
|
|
private bool IsDecoupler(Part thePart) |
private bool IsDecoupler(Part thePart) |
{ |
{ |
return thePart.HasModule<ModuleDecouple>() || |
return thePart.HasModule<ModuleDecouple>() || |
thePart.HasModule<ModuleAnchoredDecoupler>(); |
thePart.HasModule<ModuleAnchoredDecoupler>(); |
} |
} |
|
|
private bool IsFairing(Part thePart) |
private bool IsFairing(Part thePart) |
{ |
{ |
return thePart.HasModule<ModuleProceduralFairing>(); |
return thePart.HasModule<ModuleProceduralFairing>(); |
} |
} |
|
|
private bool IsSepratron() |
private bool IsSepratron() |
{ |
{ |
if (!part.ActivatesEvenIfDisconnected) |
if (!part.ActivatesEvenIfDisconnected) |
{ |
{ |
return false; |
return false; |
} |
} |
|
|
if (part is SolidRocket) |
if (part is SolidRocket) |
{ |
{ |
return true; |
return true; |
} |
} |
|
|
IEnumerable<ModuleEngines> modList = part.Modules.OfType<ModuleEngines>(); |
IEnumerable<ModuleEngines> modList = part.Modules.OfType<ModuleEngines>(); |
if (modList.Count() == 0) |
if (modList.Count() == 0) |
{ |
{ |
return false; |
return false; |
} |
} |
|
|
if (modList.First().throttleLocked) |
if (modList.First().throttleLocked) |
{ |
{ |
return true; |
return true; |
} |
} |
|
|
return false; |
return false; |
} |
} |
} |
} |
} |
} |