//
// 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;
}
}
}
}