/// <summary> /// Loads all the contact type nodes and creates ContractType objects. /// </summary> private IEnumerator <YieldInstruction> LoadContractTypeConfig() { LoggingUtil.LogDebug(this, "Loading CONTRACT_TYPE nodes."); ConfigNode[] contractConfigs = GameDatabase.Instance.GetConfigNodes("CONTRACT_TYPE"); totalContracts = contractConfigs.Count(); // First pass - create all the ContractType objects foreach (ConfigNode contractConfig in contractConfigs) { // Create the initial contract type LoggingUtil.LogVerbose(this, "Pre-load for node: '{0}'", contractConfig.GetValue("name")); try { ContractType contractType = new ContractType(contractConfig.GetValue("name")); } catch (ArgumentException) { LoggingUtil.LogError(this, "Couldn't load CONTRACT_TYPE '{0}' due to a duplicate name.", contractConfig.GetValue("name")); } } // Second pass - do the actual loading of details foreach (ConfigNode contractConfig in contractConfigs) { attemptedContracts++; if (reloading) { yield return(new WaitForEndOfFrame()); } // Fetch the contractType string name = contractConfig.GetValue("name"); ContractType contractType = ContractType.GetContractType(name); if (contractType != null && !contractType.loaded) { // Perform the load try { contractType.Load(contractConfig); if (contractType.enabled) { successContracts++; } } catch (Exception e) { LoggingUtil.LogException(e); } } } LoggingUtil.LogInfo(this, "Loaded {0} out of {1} CONTRACT_TYPE nodes.", successContracts, totalContracts); // Check for empty groups and warn foreach (ContractGroup group in ContractGroup.contractGroups.Values.Where(g => g != null)) { group.CheckEmpty(); } // Load other things MissionControlUI.GroupContainer.LoadConfig(); if (!reloading && LoggingUtil.logLevel == LoggingUtil.LogLevel.DEBUG || LoggingUtil.logLevel == LoggingUtil.LogLevel.VERBOSE) { ScreenMessages.PostScreenMessage("Contract Configurator: Loaded " + successContracts + " out of " + totalContracts + " contracts successfully.", 5, ScreenMessageStyle.UPPER_CENTER); } }
/// <summary> /// Attempts to parse a value from the config node. Returns a default value if not found. /// Validates return values using the given function. /// </summary> /// <typeparam name="T">The type of value to convert to.</typeparam> /// <param name="configNode">The ConfigNode to read from.</param> /// <param name="key">The key to examine.</param> /// <param name="setter">Function used to set the output value</param> /// <param name="obj">Factory object for error messages.</param> /// <param name="defaultValue">Default value to use if there is no key in the config node</param> /// <param name="validation">Validation function to run against the returned value</param> /// <returns>The parsed value (or default value if not found)</returns> public static bool ParseValue <T>(ConfigNode configNode, string key, Action <T> setter, IContractConfiguratorFactory obj, T defaultValue, Func <T, bool> validation) { // Initialize the data type of the expression if (currentDataNode != null && !currentDataNode.IsInitialized(key)) { currentDataNode.BlankInit(key, typeof(T)); } bool valid = true; T value = defaultValue; if (configNode.HasValue(key) || configNode.HasNode(key)) { try { // Check whether there's a value if (configNode.HasValue(key) && string.IsNullOrEmpty(configNode.GetValue(key))) { LoggingUtil.LogError(obj, "{0}: Required value '{1}' is empty.", obj.ErrorPrefix(configNode), key); valid = false; } else { // Load value value = ParseValue <T>(configNode, key, true); } // If value was non-null, run validation if (value != null && (typeof(T) != typeof(string) || ((string)(object)value) != "")) { try { valid = validation.Invoke(value); if (!valid) { // In general, the validation function should throw an exception and give a much better message LoggingUtil.LogError(obj, "{0}: A validation error occured while loading the key '{1}' with value '{2}'.", obj.ErrorPrefix(configNode), key, value); } } catch (Exception e) { if (e is DataNode.ValueNotInitialized) { throw; } LoggingUtil.LogError(obj, "{0}: A validation error occured while loading the key '{1}' with value '{2}'.", obj.ErrorPrefix(configNode), key, value); LoggingUtil.LogException(e); valid = false; } } } catch (Exception e) { if (e.GetType() == typeof(DataNode.ValueNotInitialized)) { string dependency = ((DataNode.ValueNotInitialized)e).key; string path = currentDataNode.Path() + key; LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "Trying to load {0}, but {1} is uninitialized.", path, dependency); // Defer loading this value DeferredLoadObject <T> loadObj = null; if (!deferredLoads.ContainsKey(path) || deferredLoads[path].GetType().GetGenericArguments().First() != typeof(T)) { deferredLoads[path] = new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode); } loadObj = (DeferredLoadObject <T>)deferredLoads[path]; // New dependency - try again if (!loadObj.dependencies.Contains(dependency)) { LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), " New dependency, will re-attempt to load later."); loadObj.dependencies.Add(dependency); return(true); } } LoggingUtil.LogError(obj, "{0}: Error parsing {1}", obj.ErrorPrefix(configNode), key); // Return immediately on deferred load error if (e.GetType() == typeof(DataNode.ValueNotInitialized)) { DataNode.ValueNotInitialized vni = e as DataNode.ValueNotInitialized; LoggingUtil.LogException(new Exception(StringBuilderCache.Format("Unknown identifier '@{0}'.", vni.key))); return(false); } LoggingUtil.LogException(e); valid = false; } finally { AddFoundKey(configNode, key); } } // Store the value if (currentDataNode != null) { currentDataNode[key] = value; if (!currentDataNode.IsDeterministic(key) && initialLoad) { currentDataNode.DeferredLoads.Add(new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode)); } } // Invoke the setter function if (valid) { setter.Invoke(value); } return(valid); }
/// <summary> /// Tests whether a contract can be offered. /// </summary> /// <param name="contract">The contract</param> /// <returns>Whether the contract can be offered.</returns> public bool MeetRequirements(ConfiguredContract contract) { // Hash check if (contract.ContractState == Contract.State.Offered && contract.hash != hash) { LoggingUtil.LogDebug(this, "Cancelling offered contract of type " + name + ", contract definition changed."); return(false); } // Check prestige if (prestige.Count > 0 && !prestige.Contains(contract.Prestige)) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + name + ", wrong prestige level."); return(false); } // Checks for maxSimultaneous/maxCompletions if (maxSimultaneous != 0 || maxCompletions != 0) { // Get the count of active contracts - excluding ours int activeContracts = ContractSystem.Instance.GetCurrentContracts <ConfiguredContract>(). Count(c => c.contractType != null && c.contractType.name == name); if (contract.ContractState == Contract.State.Offered || contract.ContractState == Contract.State.Active) { activeContracts--; } // Check if we're breaching the active limit if (maxSimultaneous != 0 && activeContracts >= maxSimultaneous) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + name + ", too many active contracts."); return(false); } // Check if we're breaching the completed limit if (maxCompletions != 0) { int finishedContracts = ContractSystem.Instance.GetCompletedContracts <ConfiguredContract>(). Count(c => c.contractType != null && c.contractType.name == name); if (finishedContracts + activeContracts >= maxCompletions) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + name + ", too many completed/active/offered contracts."); return(false); } } } // Check the group values if (group != null) { // Check the group active limit int activeContracts = ContractSystem.Instance.GetCurrentContracts <ConfiguredContract>().Count(c => c.contractType != null && c.contractType.group == group); if (contract.ContractState == Contract.State.Offered || contract.ContractState == Contract.State.Active) { activeContracts--; } if (group.maxSimultaneous != 0 && activeContracts >= group.maxSimultaneous) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + name + ", too many active contracts in group."); return(false); } // Check the group completed limit if (group.maxCompletions != 0) { int finishedContracts = ContractSystem.Instance.GetCompletedContracts <ConfiguredContract>().Count(c => c.contractType != null && c.contractType.group == group); if (finishedContracts + activeContracts >= maxCompletions) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + name + ", too many completed contracts in group."); return(false); } } } // Check special values are not null if (contract.ContractState != Contract.State.Active) { foreach (KeyValuePair <string, bool> pair in dataValues) { // Only check if it is a required value if (pair.Value) { string name = pair.Key; object o = dataNode[name]; if (o == null) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + this.name + ", '" + name + "' was null."); return(false); } else if (o == typeof(List <>)) { PropertyInfo prop = o.GetType().GetProperty("Count"); int count = (int)prop.GetValue(o, null); if (count == 0) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + this.name + ", '" + name + "' had zero count."); return(false); } } else if (o == typeof(Vessel)) { Vessel v = (Vessel)o; if (v.state == Vessel.State.DEAD) { LoggingUtil.LogVerbose(this, "Didn't generate contract type " + this.name + ", vessel '" + v.vesselName + "' is dead."); return(false); } } } } } // Check the captured requirements return(ContractRequirement.RequirementsMet(contract, this, requirements)); }
/// <summary> /// Checks if the "extended" requirements that change due to expressions. /// </summary> /// <param name="contract">The contract</param> /// <returns>Whether the contract can be offered.</returns> public bool MeetExtendedRequirements(ConfiguredContract contract, ContractType contractType) { LoggingUtil.LogLevel origLogLevel = LoggingUtil.logLevel; try { // Turn tracing on if (trace) { LoggingUtil.logLevel = LoggingUtil.LogLevel.VERBOSE; LoggingUtil.LogWarning(this, "Tracing enabled for contract type " + name); } // Hash check if (contract.ContractState == Contract.State.Offered && contract.hash != hash) { throw new ContractRequirementException("Contract definition changed."); } // Check prestige if (prestige.Count > 0 && !prestige.Contains(contract.Prestige)) { throw new ContractRequirementException("Wrong prestige level."); } // Do a Research Bodies check, if applicable ResearchBodiesCheck(contract); // Check special values are not null if (contract.contractType == null) { foreach (KeyValuePair <string, DataValueInfo> pair in dataValues) { // Only check if it is a required value if (pair.Value.required) { CheckRequiredValue(pair.Key); } } } if (contract.contractType == null || contract.ContractState == Contract.State.Generated || contract.ContractState == Contract.State.Withdrawn) { // Check for unique values against other contracts of the same type foreach (KeyValuePair <string, DataNode.UniquenessCheck> pair in uniquenessChecks.Where(p => contract.uniqueData.ContainsKey(p.Key))) { string key = pair.Key; DataNode.UniquenessCheck uniquenessCheck = pair.Value; LoggingUtil.LogVerbose(this, "Doing unique value check for " + key); // Get the active/offered contract lists IEnumerable <ConfiguredContract> contractList = ConfiguredContract.CurrentContracts. Where(c => c != null && c.contractType != null && c != contract); // Add in finished contracts if (uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ALL || uniquenessCheck == DataNode.UniquenessCheck.GROUP_ALL) { contractList = contractList.Union(ContractSystem.Instance.ContractsFinished.OfType <ConfiguredContract>(). Where(c => c != null && c.contractType != null && c != contract)); } // Filter anything that doesn't have our key contractList = contractList.Where(c => c.uniqueData.ContainsKey(key)); // Check for contracts of the same type if (uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ALL || uniquenessCheck == DataNode.UniquenessCheck.CONTRACT_ACTIVE) { contractList = contractList.Where(c => c.contractType.name == name); } // Check for a shared group else if (contractType.group != null) { contractList = contractList.Where(c => c.contractType.group != null && c.contractType.group.name == contractType.group.name); } // Shared lack of group else { contractList = contractList.Where(c => c.contractType.group == null); } object val = contract.uniqueData[key]; if (val != null) { // Special case for vessels - convert to the Guid Vessel vessel = val as Vessel; if (vessel != null) { val = vessel.id; } foreach (ConfiguredContract otherContract in contractList) { if (val.Equals(otherContract.uniqueData[key])) { throw new ContractRequirementException("Failed on unique value check for key '" + key + "'."); } } } } } // Check the captured requirements if (!ContractRequirement.RequirementsMet(contract, this, contract.requirements != null ? contract.requirements : requirements)) { throw new ContractRequirementException("Failed on contract requirement check."); } return(true); } catch (ContractRequirementException e) { LoggingUtil.LogLevel level = contract.ContractState == Contract.State.Active ? LoggingUtil.LogLevel.INFO : contract.contractType != null ? LoggingUtil.LogLevel.DEBUG : LoggingUtil.LogLevel.VERBOSE; string prefix = contract.contractType != null ? "Cancelling contract of type " + name + " (" + contract.Title + "): " : "Didn't generate contract of type " + name + ": "; LoggingUtil.Log(level, this.GetType(), prefix + e.Message); return(false); } catch { LoggingUtil.LogError(this, "Exception while attempting to check requirements of contract type " + name); throw; } finally { LoggingUtil.logLevel = origLogLevel; loaded = true; } }
/// <summary> /// Loads all the contact configuration nodes and creates ContractType objects. /// </summary> private IEnumerator <YieldInstruction> LoadContractConfig() { // Load all the contract groups LoggingUtil.LogDebug(this, "Loading CONTRACT_GROUP nodes."); ConfigNode[] contractGroups = GameDatabase.Instance.GetConfigNodes("CONTRACT_GROUP"); foreach (ConfigNode groupConfig in contractGroups) { // Create the group string name = groupConfig.GetValue("name"); LoggingUtil.LogInfo(this, "Loading CONTRACT_GROUP: '" + name + "'"); ContractGroup contractGroup = null; try { contractGroup = new ContractGroup(name); } catch (ArgumentException) { LoggingUtil.LogError(this, "Couldn't load CONTRACT_GROUP '" + name + "' due to a duplicate name."); } // Peform the actual load if (contractGroup != null) { bool success = false; try { ConfigNodeUtil.ClearCache(true); success = contractGroup.Load(groupConfig); } catch (Exception e) { Exception wrapper = new Exception("Error loading CONTRACT_GROUP '" + name + "'", e); LoggingUtil.LogException(wrapper); } finally { if (!success) { ContractGroup.contractGroups.Remove(name); } } } } LoggingUtil.LogDebug(this, "Loading CONTRACT_TYPE nodes."); ConfigNode[] contractConfigs = GameDatabase.Instance.GetConfigNodes("CONTRACT_TYPE"); totalContracts = contractConfigs.Count(); // First pass - create all the ContractType objects foreach (ConfigNode contractConfig in contractConfigs) { // Create the initial contract type LoggingUtil.LogVerbose(this, "Pre-load for node: '" + contractConfig.GetValue("name") + "'"); try { new ContractType(contractConfig.GetValue("name")); } catch (ArgumentException) { LoggingUtil.LogError(this, "Couldn't load CONTRACT_TYPE '" + contractConfig.GetValue("name") + "' due to a duplicate name."); } } // Second pass - do the actual loading of details foreach (ConfigNode contractConfig in contractConfigs) { attemptedContracts++; yield return(new WaitForEndOfFrame()); // Fetch the contractType string name = contractConfig.GetValue("name"); ContractType contractType = ContractType.GetContractType(name); if (contractType != null && !contractType.loaded) { // Perform the load try { contractType.Load(contractConfig); if (contractType.enabled) { successContracts++; } } catch (Exception e) { LoggingUtil.LogException(e); } } } LoggingUtil.LogInfo(this, "Loaded " + successContracts + " out of " + totalContracts + " CONTRACT_TYPE nodes."); // Check for empty groups and warn foreach (ContractGroup group in ContractGroup.contractGroups.Values.Where(g => g != null)) { group.CheckEmpty(); } // Load other things MissionControlUI.GroupContainer.LoadConfig(); // Emit settings for the menu SettingsBuilder.EmitSettings(); if (!reloading && LoggingUtil.logLevel == LoggingUtil.LogLevel.DEBUG || LoggingUtil.logLevel == LoggingUtil.LogLevel.VERBOSE) { ScreenMessages.PostScreenMessage("Contract Configurator: Loaded " + successContracts + " out of " + totalContracts + " contracts successfully.", 5, ScreenMessageStyle.UPPER_CENTER); } }
/// <summary> /// Attempts to parse a value from the config node. Returns a default value if not found. /// Validates return values using the given function. /// </summary> /// <typeparam name="T">The type of value to convert to.</typeparam> /// <param name="configNode">The ConfigNode to read from.</param> /// <param name="key">The key to examine.</param> /// <param name="setter">Function used to set the output value</param> /// <param name="obj">Factory object for error messages.</param> /// <param name="defaultValue">Default value to use if there is no key in the config node</param> /// <param name="validation">Validation function to run against the returned value</param> /// <returns>The parsed value (or default value if not found)</returns> public static bool ParseValue <T>(ConfigNode configNode, string key, Action <T> setter, IContractConfiguratorFactory obj, T defaultValue, Func <T, bool> validation) { // Initialize the data type of the expression if (currentDataNode != null && !currentDataNode.IsInitialized(key)) { currentDataNode.BlankInit(key, typeof(T)); } bool valid = true; T value = defaultValue; if (configNode.HasValue(key)) { try { // Load value value = ParseValue <T>(configNode, key, true); // If value was non-null, run validation if (value != null) { try { valid = validation.Invoke(value); if (!valid) { // In general, the validation function should throw an exception and give a much better message LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": The value supplied for " + key + " (" + value + ") is invalid."); } } catch (Exception e) { LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": The value supplied for " + key + " (" + value + ") is invalid."); LoggingUtil.LogException(e); valid = false; } } } catch (Exception e) { if (e.GetType() == typeof(DataNode.ValueNotInitialized)) { string dependency = ((DataNode.ValueNotInitialized)e).key; string path = currentDataNode.Path() + key; LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "Trying to load " + key + ", but " + dependency + " is uninitialized."); // Defer loading this value DeferredLoadObject <T> loadObj = null; if (!deferredLoads.ContainsKey(path)) { deferredLoads[path] = new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode); } loadObj = (DeferredLoadObject <T>)deferredLoads[path]; // New dependency - try again if (!loadObj.dependencies.Contains(dependency)) { LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), " New dependency, will re-attempt to load later."); loadObj.dependencies.Add(dependency); return(true); } } LoggingUtil.LogError(obj, obj.ErrorPrefix(configNode) + ": Error parsing " + key); LoggingUtil.LogException(e); // Return immediately on deferred load error if (e.GetType() == typeof(DataNode.ValueNotInitialized)) { return(false); } valid = false; } finally { AddFoundKey(configNode, key); } } // Store the value if (currentDataNode != null) { LoggingUtil.LogVerbose(typeof(ConfigNodeUtil), "DataNode[" + currentDataNode.Name + "], storing " + key + " = " + value); currentDataNode[key] = value; if (!currentDataNode.IsDeterministic(key) && initialLoad) { currentDataNode.DeferredLoads.Add(new DeferredLoadObject <T>(configNode, key, setter, obj, validation, currentDataNode)); } } // Invoke the setter function if (valid) { setter.Invoke(value); } return(valid); }
/* * Loads all the contact configuration nodes and creates ContractType objects. */ void LoadContractConfig() { LoggingUtil.LogDebug(this.GetType(), "Loading CONTRACT_TYPE nodes."); ConfigNode[] contractConfigs = GameDatabase.Instance.GetConfigNodes("CONTRACT_TYPE"); // First pass - create all the ContractType objects foreach (ConfigNode contractConfig in contractConfigs) { totalContracts++; LoggingUtil.LogVerbose(this.GetType(), "Pre-load for node: '" + contractConfig.GetValue("name") + "'"); // Create the initial contract type try { ContractType contractType = new ContractType(contractConfig.GetValue("name")); } catch (ArgumentException) { LoggingUtil.LogError(this.GetType(), "Couldn't load CONTRACT_TYPE '" + contractConfig.GetValue("name") + "' due to a duplicate name."); // BUG: The same contract will get loaded twice, but just decrement the success counter so one shows as failed successContracts--; } } // Second pass - do the actual loading of details foreach (ConfigNode contractConfig in contractConfigs) { // Fetch the contractType string name = contractConfig.GetValue("name"); ContractType contractType = ContractType.contractTypes[name]; bool success = false; if (contractType != null) { LoggingUtil.LogDebug(this.GetType(), "Loading CONTRACT_TYPE: '" + name + "'"); // Perform the load try { if (contractType.Load(contractConfig)) { successContracts++; success = true; } } catch (Exception e) { Exception wrapper = new Exception("Error loading CONTRACT_TYPE '" + name + "'", e); Debug.LogException(wrapper); } finally { if (!success) { ContractType.contractTypes.Remove(name); } } } } LoggingUtil.LogInfo(this.GetType(), "Loaded " + successContracts + " out of " + totalContracts + " CONTRACT_TYPE nodes."); if (!reloading && LoggingUtil.logLevel == LoggingUtil.LogLevel.DEBUG || LoggingUtil.logLevel == LoggingUtil.LogLevel.VERBOSE) { ScreenMessages.PostScreenMessage("Contract Configurator: Loaded " + successContracts + " out of " + totalContracts + " contracts successfully.", 5, ScreenMessageStyle.UPPER_CENTER); } }
private IEnumerator <ContractType> ReloadContractTypes() { reloading = true; // Infrom the player of the reload process ScreenMessages.PostScreenMessage("Reloading game database...", 2, ScreenMessageStyle.UPPER_CENTER); yield return(null); GameDatabase.Instance.Recompile = true; GameDatabase.Instance.StartLoad(); // Wait for the reload while (!GameDatabase.Instance.IsReady()) { yield return(null); } // Attempt to find module manager and do their reload var allMM = from loadedAssemblies in AssemblyLoader.loadedAssemblies let assembly = loadedAssemblies.assembly where assembly.GetName().Name.StartsWith("ModuleManager") orderby assembly.GetName().Version descending, loadedAssemblies.path ascending select loadedAssemblies; // Reload module manager if (allMM.Count() > 0) { ScreenMessages.PostScreenMessage("Reloading module manager...", 2, ScreenMessageStyle.UPPER_CENTER); Assembly mmAssembly = allMM.First().assembly; LoggingUtil.LogVerbose(this, "Reloading config using ModuleManager: " + mmAssembly.FullName); // Get the module manager object Type mmPatchType = mmAssembly.GetType("ModuleManager.MMPatchLoader"); UnityEngine.Object mmPatchLoader = FindObjectOfType(mmPatchType); // Get the methods MethodInfo methodStartLoad = mmPatchType.GetMethod("StartLoad", Type.EmptyTypes); MethodInfo methodIsReady = mmPatchType.GetMethod("IsReady"); // Start the load methodStartLoad.Invoke(mmPatchLoader, null); // Wait for it to complete while (!(bool)methodIsReady.Invoke(mmPatchLoader, null)) { yield return(null); } } ScreenMessages.PostScreenMessage("Reloading contract types...", 1.5f, ScreenMessageStyle.UPPER_CENTER); // Reload contract configurator ClearContractConfig(); LoadContractConfig(); AdjustContractTypes(); // We're done! ScreenMessages.PostScreenMessage("Loaded " + successContracts + " out of " + totalContracts + " contracts successfully.", 5, ScreenMessageStyle.UPPER_CENTER); reloading = false; }
public static void RemoveKerbal(ProtoCrewMember pcm) { LoggingUtil.LogVerbose(typeof(Kerbal), "Removing kerbal " + pcm.name + "..."); Vessel vessel = FlightGlobals.Vessels.Where(v => v.GetVesselCrew().Contains(pcm)).FirstOrDefault(); if (vessel != null) { // If it's an EVA make them disappear... if (vessel.isEVA) { FlightGlobals.Vessels.Remove(vessel); } else { if (vessel.loaded) { foreach (Part p in vessel.parts) { if (p.protoModuleCrew.Contains(pcm)) { // Command seats if (p.partName == "kerbalEVA") { vessel.parts.Remove(p); } // Everything else else { LoggingUtil.LogVerbose(typeof(Kerbal), " Removing " + pcm.name + " from vessel " + vessel.vesselName); p.RemoveCrewmember(pcm); } break; } } } else { foreach (ProtoPartSnapshot pps in vessel.protoVessel.protoPartSnapshots) { if (pps.HasCrew(pcm.name)) { // Command seats if (pps.partName == "kerbalEVA") { vessel.protoVessel.protoPartSnapshots.Remove(pps); } // Everything else else { LoggingUtil.LogVerbose(typeof(Kerbal), " Removing " + pcm.name + " from vessel " + vessel.vesselName); pps.RemoveCrew(pcm); } break; } } } } } // Remove the kerbal from the roster HighLogic.CurrentGame.CrewRoster.Remove(pcm.name); }