// // Kerbal Engineer Redux // // Copyright (C) 2014 CYBUTEK // // 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 // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // namespace KerbalEngineer.Extensions { using System; using System.Collections.Generic; using CompoundParts; public static class PartExtensions { //private static Part cachePart; //private static PartModule cachePartModule; //private static PartResource cachePartResource; /// /// Gets whether the part contains a specific resource. /// public static bool ContainsResource(this Part part, int resourceId) { return part.Resources.Contains(resourceId); } /// /// Gets whether the part contains resources. /// public static bool ContainsResources(this Part part) { for (int i = 0; i < part.Resources.list.Count; ++i) { if (part.Resources.list[i].amount > 0.0) { return true; } } return false; } /// /// Gets whether the part has fuel. /// public static bool EngineHasFuel(this Part part) { PartModule cachePartModule = GetModule(part); if (cachePartModule != null) { return (cachePartModule as ModuleEngines).getFlameoutState; } cachePartModule = GetModuleMultiModeEngine(part); if (cachePartModule != null) { return (cachePartModule as ModuleEnginesFX).getFlameoutState; } return false; } /// /// Gets the cost of the part excluding resources. /// public static double GetCostDry(this Part part) { return part.partInfo.cost - GetResourceCostMax(part) + part.GetModuleCosts(0.0f); } /// /// Gets the cost of the part including maximum resources. /// public static double GetCostMax(this Part part) { return part.partInfo.cost + part.GetModuleCosts(0.0f); } /// /// Gets the cost of the part modules /// Same as stock but without mem allocation /// public static double GetModuleCostsNoAlloc(this Part part, float defaultCost) { float cost = 0f; for (int i = 0; i < part.Modules.Count; i++) { PartModule pm = part.Modules[i]; if (pm is IPartCostModifier) cost += (pm as IPartCostModifier).GetModuleCost(defaultCost); } return cost; } /// /// Gets the cost of the part including resources. /// public static double GetCostWet(this Part part) { return part.partInfo.cost - GetResourceCostInverted(part) + part.GetModuleCostsNoAlloc(0.0f); // part.GetModuleCosts allocate 44B per call. } /// /// Gets the dry mass of the part. /// public static double GetDryMass(this Part part) { return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass : 0d; } /// /// Gets the maximum thrust of the part if it's an engine. /// public static double GetMaxThrust(this Part part) { PartModule cachePartModule = GetModule(part); if (cachePartModule != null) { return (cachePartModule as ModuleEngines).maxThrust; } cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule(part); if (cachePartModule != null) { return (cachePartModule as ModuleEnginesFX).maxThrust; } return 0.0; } /// /// Gets the first typed PartModule in the part's module list. /// public static T GetModule(this Part part) where T : PartModule { for (int i = 0; i < part.Modules.Count; i++) { PartModule pm = part.Modules[i]; if (pm is T) return (T)pm; } return null; } /// /// Gets a typed PartModule. /// public static T GetModule(this Part part, string className) where T : PartModule { return part.Modules[className] as T; } /// /// Gets a typed PartModule. /// public static T GetModule(this Part part, int classId) where T : PartModule { return part.Modules[classId] as T; } /// /// Gets a ModuleAlternator typed PartModule. /// public static ModuleAlternator GetModuleAlternator(this Part part) { return GetModule(part); } /// /// Gets a ModuleDeployableSolarPanel typed PartModule. /// public static ModuleDeployableSolarPanel GetModuleDeployableSolarPanel(this Part part) { return GetModule(part); } /// /// Gets a ModuleEngines typed PartModule. /// public static ModuleEngines GetModuleEngines(this Part part) { return GetModule(part); } public static ModuleEnginesFX GetModuleEnginesFx(this Part part) { return GetModule(part); } /// /// Gets a ModuleGenerator typed PartModule. /// public static ModuleGenerator GetModuleGenerator(this Part part) { return GetModule(part); } /// /// Gets a ModuleGimbal typed PartModule. /// public static ModuleGimbal GetModuleGimbal(this Part part) { return GetModule(part); } /// /// Gets the current selected ModuleEnginesFX. /// public static ModuleEnginesFX GetModuleMultiModeEngine(this Part part) { ModuleEnginesFX moduleEngineFx; string mode = GetModule(part).mode; for (int i = 0; i < part.Modules.Count; ++i) { moduleEngineFx = part.Modules[i] as ModuleEnginesFX; if (moduleEngineFx != null && moduleEngineFx.engineID == mode) { return moduleEngineFx; } } return null; } /// /// Gets a ModuleParachute typed PartModule. /// public static ModuleParachute GetModuleParachute(this Part part) { return GetModule(part); } public static ModuleRCS GetModuleRcs(this Part part) { return GetModule(part); } /// /// Gets a typed list of PartModules. /// public static List GetModules(this Part part) where T : PartModule { List list = new List(); for (int i = 0; i < part.Modules.Count; ++i) { T module = part.Modules[i] as T; if (module != null) { list.Add(module); } } return list; } public static ProtoModuleDecoupler GetProtoModuleDecoupler(this Part part) { PartModule cachePartModule = GetModule(part); if (cachePartModule == null) { cachePartModule = GetModule(part); } if (cachePartModule != null) { return new ProtoModuleDecoupler(cachePartModule); } return null; } /// /// Gets a generic proto engine for the current engine module attached to the part. /// public static ProtoModuleEngine GetProtoModuleEngine(this Part part) { PartModule cachePartModule = GetModule(part); if (cachePartModule != null) { return new ProtoModuleEngine(cachePartModule); } cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule(part); if (cachePartModule != null) { return new ProtoModuleEngine(cachePartModule); } return null; } /// /// Gets the cost of the part's contained resources. /// public static double GetResourceCost(this Part part) { double cost = 0.0; for (int i = 0; i < part.Resources.list.Count; ++i) { PartResource cachePartResource = part.Resources.list[i]; cost = cost + (cachePartResource.amount * cachePartResource.info.unitCost); } return cost; } /// /// Gets the cost of the part's contained resources, inverted. /// public static double GetResourceCostInverted(this Part part) { double sum = 0; for (int i = 0; i < part.Resources.list.Count; i++) { PartResource r = part.Resources.list[i]; sum += (r.maxAmount - r.amount) * r.info.unitCost; } return sum; } /// /// Gets the cost of the part's maximum contained resources. /// public static double GetResourceCostMax(this Part part) { double cost = 0.0; for (int i = 0; i < part.Resources.list.Count; ++i) { PartResource cachePartResource = part.Resources.list[i]; cost = cost + (cachePartResource.maxAmount * cachePartResource.info.unitCost); } return cost; } /// /// Gets the current specific impulse for the engine. /// public static double GetSpecificImpulse(this Part part, float atmosphere) { PartModule cachePartModule = GetModule(part); if (cachePartModule != null) { return (cachePartModule as ModuleEngines).atmosphereCurve.Evaluate(atmosphere); } cachePartModule = GetModuleMultiModeEngine(part) ?? GetModule(part); if (cachePartModule != null) { return (cachePartModule as ModuleEnginesFX).atmosphereCurve.Evaluate(atmosphere); } return 0.0; } /// /// Gets the total mass of the part including resources. /// public static double GetWetMass(this Part part) { return (part.physicalSignificance == Part.PhysicalSignificance.FULL) ? part.mass + part.GetResourceMass() : part.GetResourceMass(); } /// /// Gets whether the part contains a PartModule. /// public static bool HasModule(this Part part) where T : PartModule { for (int i = 0; i < part.Modules.Count; i++) { if (part.Modules[i] is T) return true; } return false; } /// /// Gets whether the part contains a PartModule conforming to the supplied predicate. /// public static bool HasModule(this Part part, Func predicate) where T : PartModule { for (int i = 0; i < part.Modules.Count; i++) { PartModule pm = part.Modules[i]; if (pm is T && predicate(pm as T)) return true; } return false; } /// /// Gets whether the part contains a PartModule. /// public static bool HasModule(this Part part, string className) { return part.Modules.Contains(className); } /// /// Gets whether the part contains a PartModule. /// public static bool HasModule(this Part part, int moduleId) { return part.Modules.Contains(moduleId); } /// /// Gets whether the part has a one shot animation. /// public static bool HasOneShotAnimation(this Part part) { PartModule cachePartModule = GetModule(part); return cachePartModule != null && (cachePartModule as ModuleAnimateGeneric).isOneShot; } /// /// Gets whether the part is a command module. /// public static bool IsCommandModule(this Part part) { return HasModule(part); } /// /// Gets whether the part is decoupled in a specified stage. /// public static bool IsDecoupledInStage(this Part part, int stage) { if ((IsDecoupler(part) || IsLaunchClamp(part)) && part.inverseStage == stage) { return true; } if (part.parent == null) { return false; } return IsDecoupledInStage(part.parent, stage); } /// /// Gets whether the part is a decoupler. /// public static bool IsDecoupler(this Part part) { return HasModule(part) || HasModule(part); } /// /// Gets whether the part is an active engine. /// public static bool IsEngine(this Part part) { return HasModule(part) || HasModule(part); } /// /// Gets whether the part is a fuel line. /// public static bool IsFuelLine(this Part part) { return HasModule(part); } /// /// Gets whether the part is a generator. /// public static bool IsGenerator(this Part part) { return HasModule(part); } /// /// Gets whether the part is a launch clamp. /// public static bool IsLaunchClamp(this Part part) { return HasModule(part); } /// /// Gets whether the part is a parachute. /// public static bool IsParachute(this Part part) { return HasModule(part); } /// /// Gets whether the part is considered a primary part on the vessel. /// public static bool IsPrimary(this Part part, List partsList, PartModule module) { for (int i = 0; i < partsList.Count; i++) { var vesselPart = partsList[i]; if (!vesselPart.HasModule(module.ClassID)) { continue; } if (vesselPart == part) { return true; } break; } return false; } public static bool IsRcsModule(this Part part) { return HasModule(part); } /// /// Gets whether the part is a sepratron. /// public static bool IsSepratron(this Part part) { return IsSolidRocket(part) && part.ActivatesEvenIfDisconnected && IsDecoupledInStage(part, part.inverseStage); } /// /// Gets whether the part is a deployable solar panel. /// public static bool IsSolarPanel(this Part part) { return HasModule(part); } /// /// Gets whether the part is a solid rocket motor. /// public static bool IsSolidRocket(this Part part) { return (part.HasModule() && part.GetModuleEngines().throttleLocked) || (part.HasModule() && part.GetModuleEnginesFx().throttleLocked); } public class ProtoModuleDecoupler { private readonly PartModule module; public ProtoModuleDecoupler(PartModule module) { this.module = module; if (this.module is ModuleDecouple) { SetModuleDecouple(); } else if (this.module is ModuleAnchoredDecoupler) { SetModuleAnchoredDecoupler(); } } public double EjectionForce { get; private set; } public bool IsOmniDecoupler { get; private set; } private void SetModuleAnchoredDecoupler() { ModuleAnchoredDecoupler decoupler = module as ModuleAnchoredDecoupler; if (decoupler == null) { return; } EjectionForce = decoupler.ejectionForce; } private void SetModuleDecouple() { ModuleDecouple decoupler = module as ModuleDecouple; if (decoupler == null) { return; } EjectionForce = decoupler.ejectionForce; IsOmniDecoupler = decoupler.isOmniDecoupler; } } public class ProtoModuleEngine { private readonly PartModule module; public ProtoModuleEngine(PartModule module) { this.module = module; if (module is ModuleEngines) { SetModuleEngines(); } } public double MaximumThrust { get; private set; } public double MinimumThrust { get; private set; } public List Propellants { get; private set; } public float GetSpecificImpulse(float atmosphere) { if (module is ModuleEngines) { return (module as ModuleEngines).atmosphereCurve.Evaluate(atmosphere); } return 0.0f; } private void SetModuleEngines() { ModuleEngines engine = module as ModuleEngines; if (engine == null) { return; } MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01); MinimumThrust = engine.minThrust; Propellants = engine.propellants; } private void SetModuleEnginesFx() { ModuleEnginesFX engine = module as ModuleEnginesFX; if (engine == null) { return; } MaximumThrust = engine.maxThrust * (engine.thrustPercentage * 0.01); MinimumThrust = engine.minThrust; Propellants = engine.propellants; } } } }