Converted foreach->for throughout.
Converted foreach->for throughout.

// EVAManager // EVAManager
// //
// EVAManager.cs // EVAManager.cs
// //
// Copyright © 2014, toadicus // Copyright © 2014, toadicus
// All rights reserved. // All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without modification, // Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met: // are permitted provided that the following conditions are met:
// //
// 1. Redistributions of source code must retain the above copyright notice, // 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer. // this list of conditions and the following disclaimer.
// //
// 2. Redistributions in binary form must reproduce the above copyright notice, // 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or other // this list of conditions and the following disclaimer in the documentation and/or other
// materials provided with the distribution. // materials provided with the distribution.
// //
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   
using KSP; using KSP;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using ToadicusTools; using ToadicusTools;
using UnityEngine; using UnityEngine;
   
namespace EVAManager namespace EVAManager
{ {
[KSPAddon(KSPAddon.Startup.MainMenu, true)] [KSPAddon(KSPAddon.Startup.MainMenu, true)]
public class EVAManager : MonoBehaviour public class EVAManager : MonoBehaviour
{ {
private const string patchPattern = @"^((DELETE|EDIT)_)?EVA_([a-zA-Z_]+)(\[(.+)\])?"; private const string patchPattern = @"^((DELETE|EDIT)_)?EVA_([a-zA-Z_]+)(\[(.+)\])?";
private const int operatorIdx = 2; private const int operatorIdx = 2;
private const int classIdx = 3; private const int classIdx = 3;
private const int nameIdx = 5; private const int nameIdx = 5;
   
private const string empty = ""; private const string empty = "";
   
private const string MODULE = "MODULE"; private const string MODULE = "MODULE";
private const string RESOURCE = "RESOURCE"; private const string RESOURCE = "RESOURCE";
   
private List<ConfigAction> evaConfigs; private List<ConfigAction> evaConfigs;
private List<ConfigAction> passQueue; private List<ConfigAction> passQueue;
   
private Part evaPart; private List<Part> evaParts;
   
private Pass pass; private Pass pass;
   
public void Awake() public void Awake()
{ {
pass = Pass.Collect; pass = Pass.Collect;
this.passQueue = new List<ConfigAction>(); this.passQueue = new List<ConfigAction>();
  this.evaParts = new List<Part>();
} }
   
public virtual void Update() public virtual void Update()
{ {
if (!PartLoader.Instance.IsReady() || PartResourceLibrary.Instance == null) if (!PartLoader.Instance.IsReady() || PartResourceLibrary.Instance == null)
{ {
return; return;
} }
   
#if DEBUG #if DEBUG
Tools.DebugLogger log; Tools.DebugLogger log;
#endif #endif
   
if (this.passQueue.Count > 0 && this.evaConfigs != null) if (this.passQueue.Count > 0 && this.evaConfigs != null)
{ {
this.evaConfigs.AddRange(this.passQueue); this.evaConfigs.AddRange(this.passQueue);
   
this.passQueue.Clear(); this.passQueue.Clear();
} }
   
  ConfigAction action;
   
switch (pass) switch (pass)
{ {
case Pass.Collect: case Pass.Collect:
foreach (var loadedPart in PartLoader.LoadedPartsList) AvailablePart loadedPart;
{ for (int idx = 0; idx < PartLoader.LoadedPartsList.Count; idx++)
if (loadedPart.name.ToLower() == "kerbaleva") {
  loadedPart = PartLoader.LoadedPartsList[operatorIdx];
  string lowerName = loadedPart.name.ToLower();
   
  if (lowerName == "kerbaleva" || lowerName == "kerbalevafemale")
{ {
this.LogDebug("Found {0}", loadedPart.name); this.LogDebug("Found {0}", loadedPart.name);
   
evaPart = loadedPart.partPrefab; evaParts.Add(loadedPart.partPrefab);
   
#if DEBUG #if DEBUG
log = Tools.DebugLogger.New(this); log = Tools.DebugLogger.New(this);
   
log.AppendLine("Modules before run:"); log.AppendLine("Modules before run:");
   
foreach (var m in evaPart.GetComponents<PartModule>()) foreach (var m in evaPart.GetComponents<PartModule>())
{ {
log.Append('\t'); log.Append('\t');
log.Append(m.GetType().Name); log.Append(m.GetType().Name);
log.Append('\n'); log.Append('\n');
} }
   
log.AppendLine("Resources before run:"); log.AppendLine("Resources before run:");
   
foreach (var r in evaPart.GetComponents<PartResource>()) foreach (var r in evaPart.GetComponents<PartResource>())
{ {
log.Append('\t'); log.Append('\t');
log.AppendFormat("Name: {0}, amount: {1}, maxAmount: {2}", log.AppendFormat("Name: {0}, amount: {1}, maxAmount: {2}",
r.resourceName, r.amount, r.maxAmount); r.resourceName, r.amount, r.maxAmount);
log.Append('\n'); log.Append('\n');
} }
   
log.Print(); log.Print();
#endif #endif
   
break; if (this.evaParts.Count == 2)
  {
  break;
  }
} }
} }
   
evaConfigs = new List<ConfigAction>(); evaConfigs = new List<ConfigAction>();
   
Regex rgx = new Regex(patchPattern); Regex rgx = new Regex(patchPattern);
   
foreach (var urlConfig in GameDatabase.Instance.root.AllConfigs) UrlDir.UrlConfig urlConfig;
{ IEnumerator<UrlDir.UrlConfig> enumerator = GameDatabase.Instance.root.AllConfigs.GetEnumerator();
  while (enumerator.MoveNext())
  {
  urlConfig = enumerator.Current;
Tools.PostDebugMessage( Tools.PostDebugMessage(
this, this,
"Checking urlconfig; name: {0}, type: {1}, config.name: {2}", "Checking urlconfig; name: {0}, type: {1}, config.name: {2}",
urlConfig.name, urlConfig.name,
urlConfig.type, urlConfig.type,
urlConfig.config.name); urlConfig.config.name);
   
Match match = rgx.Match(urlConfig.type); Match match = rgx.Match(urlConfig.type);
   
this.LogDebug("Found {0}match for {1}{2}", this.LogDebug("Found {0}match for {1}{2}",
!match.Success ? "no " : "", !match.Success ? "no " : "",
urlConfig.type, urlConfig.type,
!match.Success ? "" : string.Format( !match.Success ? "" : string.Format(
"\nOp: {0}, Class: {1}, Name: {2}", "\nOp: {0}, Class: {1}, Name: {2}",
match.Groups[operatorIdx], match.Groups[operatorIdx],
match.Groups[classIdx], match.Groups[classIdx],
match.Groups[nameIdx]) match.Groups[nameIdx])
); );
   
if (match.Success) if (match.Success)
{ {
string op = match.Groups[operatorIdx].Value; string op = match.Groups[operatorIdx].Value;
string classType = match.Groups[classIdx].Value; string classType = match.Groups[classIdx].Value;
string matchName = match.Groups[nameIdx].Value; string matchName = match.Groups[nameIdx].Value;
   
evaConfigs.Add(new ConfigAction(op, classType, matchName, urlConfig.config)); evaConfigs.Add(new ConfigAction(op, classType, matchName, urlConfig.config));
} }
} }
   
pass = Pass.Delete; pass = Pass.Delete;
break; break;
case Pass.Delete: case Pass.Delete:
foreach (ConfigAction action in this.evaConfigs) for (int idx = 0; idx < this.evaConfigs.Count; idx++)
{ {
  action = this.evaConfigs[idx];
if (action.Operator == "DELETE") if (action.Operator == "DELETE")
{ {
this.LogDebug("Trying delete action on {0}", action); this.LogDebug("Trying delete action on {0}", action);
   
if (action.MatchName == string.Empty) if (action.MatchName == string.Empty)
{ {
this.LogWarning("Match name required for 'delete' action but not present; ignoring."); this.LogWarning("Match name required for 'delete' action but not present; ignoring.");
continue; continue;
} }
   
switch (action.ClassType) switch (action.ClassType)
{ {
case MODULE: case MODULE:
this.delModuleByName(action.MatchName); this.delModuleByName(evaParts[0], action.MatchName);
  this.delModuleByName(evaParts[1], action.MatchName);
break; break;
case RESOURCE: case RESOURCE:
this.delResourceByName(action.MatchName); this.delResourceByName(evaParts[0], action.MatchName);
  this.delResourceByName(evaParts[1], action.MatchName);
break; break;
default: default:
this.LogWarning("Class type '{0}' not implemented for 'delete' action.", this.LogWarning("Class type '{0}' not implemented for 'delete' action.",
action.ClassType); action.ClassType);
continue; continue;
} }
} }
} }
   
pass = Pass.Edit; pass = Pass.Edit;
break; break;
case Pass.Edit: case Pass.Edit:
foreach (ConfigAction action in this.evaConfigs) for (int idx = 0; idx < this.evaConfigs.Count; idx++)
{ {
  action = this.evaConfigs[idx];
if (action.Operator == "EDIT") if (action.Operator == "EDIT")
{ {
this.LogDebug("Trying edit action on {0}", action); this.LogDebug("Trying edit action on {0}", action);
   
if (action.MatchName == string.Empty) if (action.MatchName == string.Empty)
{ {
this.LogWarning("Match name required for 'edit' action but not present; ignoring."); this.LogWarning("Match name required for 'edit' action but not present; ignoring.");
continue; continue;
} }
   
switch (action.ClassType) switch (action.ClassType)
{ {
case MODULE: case MODULE:
this.editModuleByNameFromConfig(action.MatchName, action.Node); this.editModuleByNameFromConfig(evaParts[0], action.MatchName, action.Node);
  this.editModuleByNameFromConfig(evaParts[1], action.MatchName, action.Node);
break; break;
case RESOURCE: case RESOURCE:
this.editResourceByNameFromConfig(action.MatchName, action.Node); this.editResourceByNameFromConfig(evaParts[0], action.MatchName, action.Node);
  this.editResourceByNameFromConfig(evaParts[1], action.MatchName, action.Node);
break; break;
default: default:
this.LogWarning("Class type '{0}' not implemented for 'delete' action.", this.LogWarning("Class type '{0}' not implemented for 'delete' action.",
action.ClassType); action.ClassType);
continue; continue;
} }
} }
} }
pass = Pass.Insert; pass = Pass.Insert;
break; break;
case Pass.Insert: case Pass.Insert:
foreach (ConfigAction action in this.evaConfigs) for (int idx = 0; idx < this.evaConfigs.Count; idx++)
{ {
  action = this.evaConfigs[idx];
if (action.Operator == empty) if (action.Operator == empty)
{ {
if (action.MatchName != string.Empty) if (action.MatchName != string.Empty)
{ {
this.LogWarning("match name ('{0}') not used for 'add' action; ignoring.", this.LogWarning("match name ('{0}') not used for 'add' action; ignoring.",
action.MatchName); action.MatchName);
} }
   
switch (action.ClassType) switch (action.ClassType)
{ {
case MODULE: case MODULE:
this.addModuleFromConfig(action.Node); this.addModuleFromConfig(evaParts[0], action.Node);
  this.addModuleFromConfig(evaParts[1], action.Node);
break; break;
case RESOURCE: case RESOURCE:
this.addResourceFromConfig(action.Node); this.addResourceFromConfig(evaParts[0], action.Node);
  this.addResourceFromConfig(evaParts[1], action.Node);
break; break;
default: default:
this.LogWarning("Class type '{0}' not implemented for 'add' action.", this.LogWarning("Class type '{0}' not implemented for 'add' action.",
action.ClassType); action.ClassType);
continue; continue;
} }
} }
} }
pass = Pass.Done; pass = Pass.Done;
break; break;
case Pass.Done: case Pass.Done:
#if DEBUG #if DEBUG
log = Tools.DebugLogger.New(this); log = Tools.DebugLogger.New(this);
   
log.AppendLine("Modules after run:"); log.AppendLine("Modules after run:");
   
foreach (var m in evaPart.GetComponents<PartModule>()) foreach (var m in evaPart.GetComponents<PartModule>())
{ {
log.Append('\t'); log.Append('\t');
log.Append(m.GetType().Name); log.Append(m.GetType().Name);
log.Append('\n'); log.Append('\n');
} }
   
log.AppendLine("Resources after run:"); log.AppendLine("Resources after run:");
   
foreach (var r in evaPart.GetComponents<PartResource>()) foreach (var r in evaPart.GetComponents<PartResource>())
{ {
log.Append('\t'); log.Append('\t');
log.AppendFormat("Name: {0}, amount: {1}, maxAmount: {2}", log.AppendFormat("Name: {0}, amount: {1}, maxAmount: {2}",
r.resourceName, r.amount, r.maxAmount); r.resourceName, r.amount, r.maxAmount);
log.Append('\n'); log.Append('\n');
} }
   
log.Print(); log.Print();
#endif #endif
   
GameObject.Destroy(this); GameObject.Destroy(this);
   
this.LogDebug("Destruction Requested."); this.LogDebug("Destruction Requested.");
break; break;
} }
} }
   
private void addModuleFromConfig(ConfigNode evaModuleNode) private void addModuleFromConfig(Part evaPart, ConfigNode evaModuleNode)
{ {
string moduleName; string moduleName;
   
if (evaModuleNode.TryGetValue("name", out moduleName)) if (evaModuleNode.TryGetValue("name", out moduleName))
{ {
if (evaPart.GetComponents<PartModule>().Any(m => m.GetType().Name == moduleName)) if (evaPart.GetComponents<PartModule>().Any(m => m.GetType().Name == moduleName))
{ {
this.LogWarning("Skipping module {1}: already present in kerbalEVA", moduleName); this.LogWarning("Skipping module {1}: already present in kerbalEVA", moduleName);
return; return;
} }
   
Type moduleClass = AssemblyLoader.GetClassByName(typeof(PartModule), moduleName); Type moduleClass = AssemblyLoader.GetClassByName(typeof(PartModule), moduleName);
   
if (moduleClass == null) if (moduleClass == null)
{ {
this.LogWarning("Skipping module {0}: class not found in loaded assemblies.", moduleName); this.LogWarning("Skipping module {0}: class not found in loaded assemblies.", moduleName);
return; return;
} }
   
try try
{ {
PartModule evaModule = evaPart.gameObject.AddComponent(moduleClass) PartModule evaModule = evaPart.gameObject.AddComponent(moduleClass)
as PartModule; as PartModule;
   
var awakeMethod = typeof(PartModule).GetMethod("Awake", var awakeMethod = typeof(PartModule).GetMethod("Awake",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance System.Reflection.BindingFlags.Instance
); );
   
awakeMethod.Invoke(evaModule, new object[] {}); awakeMethod.Invoke(evaModule, new object[] {});
   
evaModule.Load(evaModuleNode); evaModule.Load(evaModuleNode);
} }
catch (Exception ex) catch (Exception ex)
{ {
this.LogError("Handled exception {0} while adding modules to kerbalEVA.", ex.GetType().Name); this.LogError("Handled exception {0} while adding modules to kerbalEVA.", ex.GetType().Name);
   
#if DEBUG #if DEBUG
Debug.LogException(ex); Debug.LogException(ex);
#endif #endif
} }
   
if (evaPart.GetComponents<PartModule>().Any(m => m.GetType().Name == moduleName)) if (evaPart.GetComponents<PartModule>().Any(m => m.GetType().Name == moduleName))
{ {
this.Log("Added module {0} to kerbalEVA part.", moduleName); this.Log("Added module {0} to kerbalEVA part.", moduleName);
} }
else else
{ {
this.LogWarning("Failed to add {0} to kerbalEVA part.", moduleName); this.LogWarning("Failed to add {0} to kerbalEVA part.", moduleName);
} }
} }
else else
{ {
this.LogWarning("Skipping malformed EVA_MODULE node: missing 'name' field."); this.LogWarning("Skipping malformed EVA_MODULE node: missing 'name' field.");
return; return;
} }
} }
   
private void addResourceFromConfig(ConfigNode evaResourceNode) private void addResourceFromConfig(Part evaPart, ConfigNode evaResourceNode)
{ {
string resourceName; string resourceName;
   
if (evaResourceNode.TryGetValue("name", out resourceName)) if (evaResourceNode.TryGetValue("name", out resourceName))
{ {
this.LogDebug("Adding resource '{0}'", resourceName); this.LogDebug("Adding resource '{0}'", resourceName);
   
PartResourceDefinition resourceInfo = PartResourceDefinition resourceInfo =
PartResourceLibrary.Instance.GetDefinition(resourceName); PartResourceLibrary.Instance.GetDefinition(resourceName);
   
if (resourceInfo == null) if (resourceInfo == null)
{ {
this.LogWarning("Skipping resource {0}: definition not present in library.", resourceName); this.LogWarning("Skipping resource {0}: definition not present in library.", resourceName);
   
return; return;
} }
   
this.LogDebug("Resource '{0}' is in library.", resourceName); this.LogDebug("Resource '{0}' is in library.", resourceName);
   
if (evaPart.GetComponents<PartResource>().Any(r => r.resourceName == resourceName)) if (evaPart.GetComponents<PartResource>().Any(r => r.resourceName == resourceName))
{ {
this.LogWarning("Skipping resource {0}: already present in kerbalEVA.", resourceName); this.LogWarning("Skipping resource {0}: already present in kerbalEVA.", resourceName);
   
return; return;
} }
   
this.LogDebug("Resource '{0}' is not present.", resourceName); this.LogDebug("Resource '{0}' is not present.", resourceName);
   
PartResource resource = evaPart.gameObject.AddComponent<EVAPartResource>(); PartResource resource = evaPart.gameObject.AddComponent<EVAPartResource>();
   
this.LogDebug("Resource '{0}' component built.", resourceName); this.LogDebug("Resource '{0}' component built.", resourceName);
   
resource.SetInfo(resourceInfo); resource.SetInfo(resourceInfo);
((EVAPartResource)resource).Load(evaResourceNode); ((EVAPartResource)resource).Load(evaResourceNode);
   
this.Log("Added resource {0} to kerbalEVA part.", resource.resourceName); this.Log("Added resource {0} to kerbalEVA part.", resource.resourceName);
   
this.LogDebug("Resource '{0}' loaded.", resourceName); this.LogDebug("Resource '{0}' loaded.", resourceName);
} }
else else
{ {
this.Log("Skipping malformed EVA_RESOURCE node: missing 'name' field."); this.Log("Skipping malformed EVA_RESOURCE node: missing 'name' field.");
return; return;
} }
} }
   
private void delModuleByName(string matchName) private void delModuleByName(Part evaPart, string matchName)
{ {
PartModule module = this.matchFirstModuleByName(matchName); PartModule module = this.matchFirstModuleByName(evaPart, matchName);
   
if (module != null) if (module != null)
{ {
GameObject.Destroy(module); GameObject.Destroy(module);
} }
} }
   
private void delResourceByName(string matchName) private void delResourceByName(Part evaPart, string matchName)
{ {
PartResource resource = this.matchFirstResourceByName(matchName); PartResource resource = this.matchFirstResourceByName(evaPart, matchName);
   
if (resource != null) if (resource != null)
{ {
GameObject.Destroy(resource); GameObject.Destroy(resource);
   
Tools.PostDebugMessage( Tools.PostDebugMessage(
this, this,
"EVA resource {0} marked for destruction.", "EVA resource {0} marked for destruction.",
resource.resourceName); resource.resourceName);
} }
} }
   
private void editModuleByNameFromConfig(string matchName, ConfigNode config) private void editModuleByNameFromConfig(Part evaPart, string matchName, ConfigNode config)
{ {
PartModule module = this.matchFirstModuleByName(matchName); PartModule module = this.matchFirstModuleByName(evaPart, matchName);
   
if (module != null) if (module != null)
{ {
if (config.HasValue("name")) if (config.HasValue("name"))
{ {
config = this.mergeConfigs(module, config); config = this.mergeConfigs(module, config);
   
GameObject.Destroy(module); GameObject.Destroy(module);
   
this.LogDebug("EVA module {0} marked for destruction.", module.GetType().Name); this.LogDebug("EVA module {0} marked for destruction.", module.GetType().Name);
   
ConfigAction copyAction = new ConfigAction(empty, MODULE, empty, config); ConfigAction copyAction = new ConfigAction(empty, MODULE, empty, config);
   
this.passQueue.Add(copyAction); this.passQueue.Add(copyAction);
   
Tools.PostDebugMessage( Tools.PostDebugMessage(
this, this,
"EVA module {0} marked for insertion\n(action: {1})", "EVA module {0} marked for insertion\n(action: {1})",
config.GetValue("name"), config.GetValue("name"),
copyAction copyAction
); );
} }
else else
{ {
this.assignFieldsFromConfig(module, config); this.assignFieldsFromConfig(module, config);
} }
} }
} }
   
private void editResourceByNameFromConfig(string matchName, ConfigNode config) private void editResourceByNameFromConfig(Part evaPart, string matchName, ConfigNode config)
{ {
PartResource resource = this.matchFirstResourceByName(matchName); PartResource resource = this.matchFirstResourceByName(evaPart, matchName);
   
if (resource != null) if (resource != null)
{ {
if (config.HasValue("name")) if (config.HasValue("name"))
{ {
config = this.mergeConfigs(resource, config); config = this.mergeConfigs(resource, config);
   
GameObject.Destroy(resource); GameObject.Destroy(resource);
   
this.LogDebug("EVA resource {0} marked for destruction.", resource.resourceName); this.LogDebug("EVA resource {0} marked for destruction.", resource.resourceName);
   
ConfigAction copyAction = new ConfigAction(empty, RESOURCE, empty, config); ConfigAction copyAction = new ConfigAction(empty, RESOURCE, empty, config);
   
this.passQueue.Add(copyAction); this.passQueue.Add(copyAction);
   
Tools.PostDebugMessage( Tools.PostDebugMessage(
this, this,
"EVA resource {0} marked for insertion\n(action: {1})", "EVA resource {0} marked for insertion\n(action: {1})",
config.GetValue("name"), config.GetValue("name"),
copyAction copyAction
); );
} }
else else
{ {
this.assignFieldsFromConfig(resource, config); this.assignFieldsFromConfig(resource, config);
} }
} }
} }
   
private PartModule matchFirstModuleByName(string matchName) private PartModule matchFirstModuleByName(Part evaPart, string matchName)
{ {
Regex rgx = new Regex(@matchName); Regex rgx = new Regex(@matchName);
   
foreach (PartModule module in evaPart.GetComponents<PartModule>()) PartModule[] modules = evaPart.GetComponents<PartModule>();
{ PartModule module;
  for (int idx = 0; idx < modules.Length; idx++)
  {
  module = modules[idx];
Match match = rgx.Match(module.GetType().Name); Match match = rgx.Match(module.GetType().Name);
   
if (match.Success) if (match.Success)
{ {
return module; return module;
} }
} }
   
return null; return null;
} }
   
private PartResource matchFirstResourceByName(string matchName) private PartResource matchFirstResourceByName(Part evaPart, string matchName)
{ {
Regex rgx = new Regex(@matchName); Regex rgx = new Regex(@matchName);
   
foreach (PartResource resource in evaPart.GetComponents<PartResource>()) PartResource[] resources = evaPart.GetComponents<PartResource>();
{ PartResource resource;
  for (int idx = 0; idx < resources.Length; idx++)
  {
  resource = resources[idx];
Match match = rgx.Match(resource.resourceName); Match match = rgx.Match(resource.resourceName);
   
Tools.PostDebugMessage( Tools.PostDebugMessage(
this, this,
"EVA resource {0} is {1}a match for action.", "EVA resource {0} is {1}a match for action.",
resource.resourceName, resource.resourceName,
match.Success ? "" : "not "); match.Success ? "" : "not ");
   
if (match.Success) if (match.Success)
{ {
return resource; return resource;
} }
} }
   
return null; return null;
} }
   
private bool assignFieldsFromConfig(object obj, ConfigNode config) private bool assignFieldsFromConfig(object obj, ConfigNode config)
{ {
bool success = true; bool success = true;
   
foreach (ConfigNode.Value cfgValue in config.values) ConfigNode.Value cfgValue;
{ for (int idx = 0; idx < config.values.Count; idx++)
  {
  cfgValue = config.values[idx];
try try
{ {
var namedField = obj.GetType().GetField(cfgValue.name, var namedField = obj.GetType().GetField(cfgValue.name,
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.FlattenHierarchy System.Reflection.BindingFlags.FlattenHierarchy
); );
   
if (namedField != null) if (namedField != null)
{ {
var fieldType = namedField.FieldType; var fieldType = namedField.FieldType;
   
object convertedValue = Convert.ChangeType(cfgValue.value, fieldType); object convertedValue = Convert.ChangeType(cfgValue.value, fieldType);
   
namedField.SetValue(obj, convertedValue); namedField.SetValue(obj, convertedValue);
   
success &= true; success &= true;
   
this.LogDebug("Assigned field '{0}' with new value '{1}'.", namedField.Name, convertedValue); this.LogDebug("Assigned field '{0}' with new value '{1}'.", namedField.Name, convertedValue);
} }
else else
{ {
this.LogWarning("Failed assigning value '{0}': field not found in class '{1}'", this.LogWarning("Failed assigning value '{0}': field not found in class '{1}'",
cfgValue.name, cfgValue.name,
obj.GetType().Name obj.GetType().Name
); );
   
success &= false; success &= false;
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
this.LogWarning("Failed assigning value '{0}': {1}", cfgValue.name, ex.Message); this.LogWarning("Failed assigning value '{0}': {1}", cfgValue.name, ex.Message);
   
success &= false; success &= false;
   
#if DEBUG #if DEBUG
Debug.LogException(ex); Debug.LogException(ex);
#endif #endif
} }
} }
   
return success; return success;
} }
   
private ConfigNode mergeConfigs(ConfigNode source, ConfigNode target) private ConfigNode mergeConfigs(ConfigNode source, ConfigNode target)
{ {
foreach (ConfigNode.Value value in target.values) ConfigNode.Value value;
{ for (int idx = 0; idx < target.values.Count; idx++)
  {
  value = target.values[idx];
if (source.HasValue(value.name)) if (source.HasValue(value.name))
{ {
source.RemoveValue(value.name); source.RemoveValue(value.name);
} }
} }
   
source.CopyTo(target); source.CopyTo(target);
   
return target; return target;
} }
   
private ConfigNode mergeConfigs(PartResource resource, ConfigNode target) private ConfigNode mergeConfigs(PartResource resource, ConfigNode target)
{ {
ConfigNode source = new ConfigNode(); ConfigNode source = new ConfigNode();
   
if (resource is EVAPartResource) if (resource is EVAPartResource)
{ {
((EVAPartResource)resource).Save(source); ((EVAPartResource)resource).Save(source);
} }
else else
{ {
resource.Save(source); resource.Save(source);
} }
   
return this.mergeConfigs(source, target); return this.mergeConfigs(source, target);
} }
   
private ConfigNode mergeConfigs(PartModule module, ConfigNode target) private ConfigNode mergeConfigs(PartModule module, ConfigNode target)
{ {
ConfigNode source = new ConfigNode(); ConfigNode source = new ConfigNode();
   
source.AddValue("name", module.GetType().Name); source.AddValue("name", module.GetType().Name);
   
foreach (var field in module.GetType().GetFields( System.Reflection.FieldInfo[] fieldInfos = module.GetType().GetFields(
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance System.Reflection.BindingFlags.Instance
)) );
{ System.Reflection.FieldInfo field;
foreach (object attr in field.GetCustomAttributes(true)) for (int fIdx = 0; fIdx < fieldInfos.Length; fIdx++)
{ {
  field = fieldInfos[fIdx];
   
  object[] attrs = field.GetCustomAttributes(true);
  object attr;
  for (int aIdx = 0; aIdx < attrs.Length; aIdx++)
  {
  attr = attrs[aIdx];
   
if (attr is KSPField) if (attr is KSPField)
{ {
source.AddValue(field.Name, field.GetValue(module)); source.AddValue(field.Name, field.GetValue(module));
   
break; break;
} }
} }
} }
   
return this.mergeConfigs(source, target); return this.mergeConfigs(source, target);
} }
   
#if DEBUG #if DEBUG
public void OnDestroy() public void OnDestroy()
{ {
this.LogDebug("Destroyed."); this.LogDebug("Destroyed.");
} }
#endif #endif
   
private enum Pass private enum Pass
{ {
Collect, Collect,
Delete, Delete,
Edit, Edit,
Insert, Insert,
Done Done
} }
   
internal class ConfigAction internal class ConfigAction
{ {
internal string Operator internal string Operator
{ {
get; get;
private set; private set;
} }
   
internal string ClassType internal string ClassType
{ {
get; get;
private set; private set;
} }
   
internal string MatchName internal string MatchName
{ {
get; get;
private set; private set;
} }
   
internal ConfigNode Node internal ConfigNode Node
{ {
get; get;
private set; private set;
} }
   
private ConfigAction() {} private ConfigAction() {}
   
internal ConfigAction(string op, string classType, string matchName) internal ConfigAction(string op, string classType, string matchName)
{ {
this.Operator = op; this.Operator = op;
this.ClassType = classType; this.ClassType = classType;
this.MatchName = matchName; this.MatchName = matchName;
} }
   
internal ConfigAction(string op, string classType, string matchName, ConfigNode node) internal ConfigAction(string op, string classType, string matchName, ConfigNode node)
: this(op, classType, matchName) : this(op, classType, matchName)
{ {
this.Node = node; this.Node = node;
} }
   
public override string ToString() public override string ToString()
{ {
return string.Format( return string.Format(
"[ConfigAction: Operator: {0}, ClassType: {1}, MatchName: {2}, Node: {3}]", "[ConfigAction: Operator: {0}, ClassType: {1}, MatchName: {2}, Node: {3}]",
this.Operator ?? "null", this.Operator ?? "null",
this.ClassType ?? "null", this.ClassType ?? "null",
this.MatchName ?? "null", this.MatchName ?? "null",
this.Node); this.Node);
} }
} }
} }
} }
   
   
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug_win</Configuration> <Configuration Condition=" '$(Configuration)' == '' ">Debug_win</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion> <ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{7BBCD4C2-9518-437E-98E4-F0F53DE65D36}</ProjectGuid> <ProjectGuid>{7BBCD4C2-9518-437E-98E4-F0F53DE65D36}</ProjectGuid>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<RootNamespace>EVAManager</RootNamespace> <RootNamespace>EVAManager</RootNamespace>
<AssemblyName>EVAManager</AssemblyName> <AssemblyName>EVAManager</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<UseMSBuildEngine>False</UseMSBuildEngine> <UseMSBuildEngine>False</UseMSBuildEngine>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_win|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_win|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath> <OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants> <DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause> <ConsolePause>false</ConsolePause>
<CustomCommands> <CustomCommands>
<CustomCommands> <CustomCommands>
<Command type="AfterBuild" command="xcopy /y ${TargetFile} ${ProjectDir}\GameData\EVAManager\" /> <Command type="AfterBuild" command="xcopy /y ${TargetFile} ${ProjectDir}\GameData\EVAManager\" />
</CustomCommands> </CustomCommands>
</CustomCommands> </CustomCommands>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_win|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_win|AnyCPU' ">
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath> <OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause> <ConsolePause>false</ConsolePause>
<CustomCommands> <CustomCommands>
<CustomCommands> <CustomCommands>
<Command type="AfterBuild" command="xcopy /y ${TargetFile} ${ProjectDir}\GameData\EVAManager\" /> <Command type="AfterBuild" command="xcopy /y ${TargetFile} ${ProjectDir}\GameData\EVAManager\" />
</CustomCommands> </CustomCommands>
</CustomCommands> </CustomCommands>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_linux|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release_linux|AnyCPU' ">
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath> <OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause> <ConsolePause>false</ConsolePause>
<CustomCommands> <CustomCommands>
<CustomCommands> <CustomCommands>
<Command type="AfterBuild" command="cp -vfa ${TargetFile} ${ProjectDir}/GameData/EVAManager/" /> <Command type="AfterBuild" command="cp -vfa ${TargetFile} ${ProjectDir}/GameData/EVAManager/" />
</CustomCommands> </CustomCommands>
</CustomCommands> </CustomCommands>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_linux|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug_linux|AnyCPU' ">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath> <OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants> <DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause> <ConsolePause>false</ConsolePause>
<CustomCommands> <CustomCommands>
<CustomCommands> <CustomCommands>
<Command type="AfterBuild" command="cp -vfa ${TargetFile} ${ProjectDir}/GameData/EVAManager/" /> <Command type="AfterBuild" command="cp -vfa ${TargetFile} ${ProjectDir}/GameData/EVAManager/" />
</CustomCommands> </CustomCommands>
</CustomCommands> </CustomCommands>
</PropertyGroup> </PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>
<Reference Include="Assembly-CSharp"> <Reference Include="Assembly-CSharp">
<HintPath>..\_KSPAssemblies\Assembly-CSharp.dll</HintPath> <HintPath>..\_KSPAssemblies\Assembly-CSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="System"> <Reference Include="System">
<HintPath>..\_KSPAssemblies\System.dll</HintPath> <HintPath>..\_KSPAssemblies\System.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnityEngine"> <Reference Include="UnityEngine">
<HintPath>..\_KSPAssemblies\UnityEngine.dll</HintPath> <HintPath>..\_KSPAssemblies\UnityEngine.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="EVAManager.cs" /> <Compile Include="EVAManager.cs" />
<Compile Include="..\ToadicusTools\Tools.cs"> <Compile Include="..\ToadicusTools\Tools.cs">
<Link>Tools.cs</Link> <Link>Tools.cs</Link>
</Compile> </Compile>
<Compile Include="EVAPartResource.cs" /> <Compile Include="EVAPartResource.cs" />
<Compile Include="..\ToadicusTools\ConfigNodeExtensions.cs"> <Compile Include="..\ToadicusTools\ConfigNodeExtensions.cs">
<Link>ConfigNodeExtensions.cs</Link> <Link>ConfigNodeExtensions.cs</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ProjectExtensions> <ProjectExtensions>
<MonoDevelop> <MonoDevelop>
<Properties> <Properties>
<Policies> <Policies>
<StandardHeader Text="${ProjectName}&#xA;&#xA;${FileName}&#xA;&#xA;Copyright © ${Year}, ${AuthorName}&#xA;All rights reserved.&#xA;&#xA;Redistribution and use in source and binary forms, with or without modification,&#xA;are permitted provided that the following conditions are met:&#xA;&#xA;1. Redistributions of source code must retain the above copyright notice,&#xA; this list of conditions and the following disclaimer.&#xA;&#xA;2. Redistributions in binary form must reproduce the above copyright notice,&#xA; this list of conditions and the following disclaimer in the documentation and/or other&#xA; materials provided with the distribution.&#xA;&#xA;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES,&#xA;INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&#xA;DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,&#xA;SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&#xA;SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,&#xA;WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE&#xA;OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." IncludeInNewFiles="True" /> <StandardHeader Text="${ProjectName}&#xA;&#xA;${FileName}&#xA;&#xA;Copyright © ${Year}, ${AuthorName}&#xA;All rights reserved.&#xA;&#xA;Redistribution and use in source and binary forms, with or without modification,&#xA;are permitted provided that the following conditions are met:&#xA;&#xA;1. Redistributions of source code must retain the above copyright notice,&#xA; this list of conditions and the following disclaimer.&#xA;&#xA;2. Redistributions in binary form must reproduce the above copyright notice,&#xA; this list of conditions and the following disclaimer in the documentation and/or other&#xA; materials provided with the distribution.&#xA;&#xA;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES,&#xA;INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE&#xA;DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,&#xA;SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR&#xA;SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,&#xA;WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE&#xA;OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." IncludeInNewFiles="True" />
</Policies> </Policies>
</Properties> </Properties>
</MonoDevelop> </MonoDevelop>
</ProjectExtensions> </ProjectExtensions>
<ItemGroup> <ItemGroup>
<None Include="GameData\EVAManager\EVAManager.cfg" /> <Folder Include="GameData\EVAManager\" />
  </ItemGroup>
  <ItemGroup>
  <None Include="GameData\EVAManager\EVAManager.cfg.example" />
</ItemGroup> </ItemGroup>
</Project> </Project>
// EVAManager  
//  
// EVAPartModule.cfg  
//  
// Copyright © 2014, toadicus  
// All rights reserved.  
//  
// Redistribution and use in source and binary forms, with or without modification,  
// are permitted provided that the following conditions are met:  
//  
// 1. Redistributions of source code must retain the above copyright notice,  
// this list of conditions and the following disclaimer.  
//  
// 2. Redistributions in binary form must reproduce the above copyright notice,  
// this list of conditions and the following disclaimer in the documentation and/or other  
// materials provided with the distribution.  
//  
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,  
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE  
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,  
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR  
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
 
DELETE_EVA_RESOURCE[EVA\sPropellant] {}  
 
EVA_RESOURCE  
{  
name = EVA Propellant  
amount = 0  
maxAmount = 5  
}  
 
@PART[*]:HAS[#CrewCapacity[1]]  
{  
RESOURCE  
{  
name = EVA Propellant  
amount = 5  
maxAmount = 5  
}  
}  
 
@PART[*]:HAS[#CrewCapacity[2]]  
{  
RESOURCE  
{  
name = EVA Propellant  
amount = 10  
maxAmount = 10  
}  
}  
 
@PART[*]:HAS[#CrewCapacity[3]]  
{  
RESOURCE  
{  
name = EVA Propellant  
amount = 15  
maxAmount = 15  
}  
}  
 
@PART[*]:HAS[#CrewCapacity[4]]  
{  
RESOURCE  
{  
name = EVA Propellant  
amount = 20  
maxAmount = 20  
}  
}  
 
  // EVAManager
  //
  // EVAManager.cfg
  //
  // Copyright © 2014, toadicus
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without modification,
  // are permitted provided that the following conditions are met:
  //
  // 1. Redistributions of source code must retain the above copyright notice,
  // this list of conditions and the following disclaimer.
  //
  // 2. Redistributions in binary form must reproduce the above copyright notice,
  // this list of conditions and the following disclaimer in the documentation and/or other
  // materials provided with the distribution.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
  // This is a fun proof-of-concept patch set that adds EVA Propellant to parts that house Kerbals. This requires you to
  // ration EVA fuel over the course of a mission inside of individual jaunts. If you like the idea, rename this file to
  // remove the .example extension!
 
  DELETE_EVA_RESOURCE[EVA\sPropellant] {}
 
  EVA_RESOURCE
  {
  name = EVA Propellant
  amount = 0
  maxAmount = 5
  }
 
  @PART[*]:HAS[#CrewCapacity[>0]]
  {
  RESOURCE
  {
  name = EVA Propellant
  amount = 5
  @amount *= #$/CrewCapacity$
  maxAmount = 5
  @maxAmount *= #$/CrewCapacity$
  }
  }
 
  @RESOURCE_DEFINITION[EVA*Propellant]
  {
  @density = 0.000807
  }
 
 Binary files a/GameData/EVAManager/EVAManager.dll and /dev/null differ