UpgradeableFacilityConfigurator: New module that lets facility upgrade costs be configured.
UpgradeableFacilityConfigurator: New module that lets facility upgrade costs be configured.

--- a/GameData/NidaSampi/Scientific/MM/PartRedefs.cfg
+++ b/GameData/NidaSampi/Scientific/MM/PartRedefs.cfg
@@ -171,4 +171,8 @@
 	}
 }
 
+@PART[*]:HAS[#cost]:FINAL
+{
+	@cost *= 5
+}
 

--- /dev/null
+++ b/GameData/NidaSampi/Scientific/Upgradeables/Upgradeables.cfg
@@ -1,1 +1,13 @@
+// NSScientific
+// by toadicus, dudecon
+//
+// Upgradeables.cs
+//
+// This work is released into the Public Domain.
+//
 
+FACILITY_GLOBAL_MODIFIERS
+{
+	COST_MULTIPLIER = 0.25
+}
+

--- a/NSScientific.csproj
+++ b/NSScientific.csproj
@@ -95,6 +95,7 @@
     </Compile>
     <Compile Include="ModuleKerbalMassTracker.cs" />
     <Compile Include="NSSAltitudeRecordFixer.cs" />
+    <Compile Include="UpgradeableFacilityConfigurator.cs" />
   </ItemGroup>
   <ProjectExtensions>
     <MonoDevelop>
@@ -112,5 +113,9 @@
     <None Include="GameData\NidaSampi\Scientific\MM\ScienceRedefs.cfg" />
     <None Include="GameData\NidaSampi\Scientific\Resources\Resources.cfg" />
     <None Include="GameData\NidaSampi\Scientific\Textures\ATM_NSS.cfg" />
+    <None Include="GameData\NidaSampi\Scientific\Upgradeables\Upgradeables.cfg" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="GameData\NidaSampi\Scientific\Upgradeables\" />
   </ItemGroup>
 </Project>

--- /dev/null
+++ b/UpgradeableFacilityConfigurator.cs
@@ -1,1 +1,311 @@
-
+// NSScientific
+// by toadicus, dudecon
+//
+// NSSUpgradeableFacilityConfigurator.cs
+//
+// This work is released into the Public Domain.
+//
+
+using KSP;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using ToadicusTools;
+using UnityEngine;
+using Upgradeables;
+
+namespace NSScientific
+{
+	[KSPAddon(KSPAddon.Startup.SpaceCentre, true)]
+	public class UpgradeableFacilityConfigurator : MonoBehaviour
+	{
+		private const string GLOBAL_DEF = "FACILITY_GLOBAL_MODIFIERS";
+		private const string FACILITY_DEF = "FACILITY_DEF";
+		private const string FACILITY_ID = "FACILITY_ID";
+
+		private const string FACILITY_LEVEL = "FACILITY_LEVEL";
+		private const string LEVEL_IDX = "LEVEL_IDX";
+		private const string LEVEL_COST = "LEVEL_COST";
+
+		private const string COST_MULTIPLIER = "COST_MULTIPLIER";
+
+		private List<ConfigurableFacilityDef> facilityDefs;
+
+		private FieldInfo UpgradeableObjectLevelsField;
+
+		private bool runOnce;
+
+		#region MonoBehaviour LifeCycle
+		public void Awake()
+		{
+			switch (HighLogic.CurrentGame.Mode)
+			{
+				case Game.Modes.CAREER:
+					break;
+				default:
+					this.LogWarning("Invalid game mode; bailing out.");
+					this.runOnce = false;
+					GameObject.Destroy(this);
+					return;
+			}
+
+			this.UpgradeableObjectLevelsField = typeof(UpgradeableObject).GetField(
+				"upgradeLevels",
+				BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public
+			);
+
+			if (this.UpgradeableObjectLevelsField == null)
+			{
+				this.LogWarning("Could no find UpgradeableObjectLevelsField; bailing out.");
+				this.runOnce = false;
+				GameObject.Destroy(this);
+				return;
+			}
+
+			this.facilityDefs = new List<ConfigurableFacilityDef>();
+
+			this.runOnce = true;
+
+			this.LoadDefs();
+		}
+
+		public void Update()
+		{
+			if (this.runOnce && ScenarioUpgradeableFacilities.Instance != null)
+			{
+				foreach (ConfigurableFacilityDef facDef in this.facilityDefs)
+				{
+					if (ScenarioUpgradeableFacilities.protoUpgradeables.ContainsKey(facDef.FacilityId))
+					{
+						var facRefs = ScenarioUpgradeableFacilities.protoUpgradeables[facDef.FacilityId].facilityRefs;
+
+						foreach (var facRef in facRefs)
+						{
+							var facLevels = this.GetObjectUpgradeLevels(facRef);
+
+							foreach (ConfigurableLevelDef levelDef in facDef.LevelDefs)
+							{
+								if (
+									levelDef.LevelIdx == -1f ||
+									(float.IsNaN(levelDef.LevelCost) && levelDef.LevelMultiplier == 1f)
+								)
+								{
+									continue;
+								}
+
+								if (facLevels.Length > levelDef.LevelIdx)
+								{
+									var facLevel = facLevels[levelDef.LevelIdx];
+
+									if (!float.IsNaN(levelDef.LevelCost))
+									{
+										facLevel.levelCost = levelDef.LevelCost;
+									}
+									else
+									{
+										facLevel.levelCost *= levelDef.LevelMultiplier;
+									}
+
+									this.Log("Facility {0}, level {1}: Cost changed to {2}",
+										facRef.id,
+										levelDef.LevelIdx,
+										facLevel.levelCost
+									);
+								}
+							}
+
+							if (facDef.FacilityMultiplier != 1f)
+							{
+								for (int idx = 0; idx < facLevels.Length; idx++)
+								{
+									var facLevel = facLevels[idx];
+
+									facLevel.levelCost *= facDef.FacilityMultiplier;
+
+									this.Log("Facility {0}, level {1}: Cost changed to {2}",
+										facRef.id,
+										idx,
+										facLevel.levelCost
+									);
+								}
+							}
+						}
+					}
+				}
+
+				foreach (var node in GameDatabase.Instance.GetConfigNodes(GLOBAL_DEF))
+				{
+					float globalMult = node.GetValue(COST_MULTIPLIER, 1f);
+
+					if (globalMult != 1f)
+					{
+						foreach (var protoUpgradeable in ScenarioUpgradeableFacilities.protoUpgradeables.Values)
+						{
+							foreach (var facRef in protoUpgradeable.facilityRefs)
+							{
+								foreach (var facLevel in this.GetObjectUpgradeLevels(facRef))
+								{
+									facLevel.levelCost *= globalMult;
+								}
+							}
+						}
+					}
+
+					break;
+				}
+
+				this.runOnce = false;
+			}
+		}
+		#endregion
+
+		private void LoadDefs()
+		{
+			foreach (ConfigNode node in GameDatabase.Instance.GetConfigNodes(FACILITY_DEF))
+			{
+				this.facilityDefs.Add(new ConfigurableFacilityDef(node));
+			}
+		}
+
+		private UpgradeableObject.UpgradeLevel[] GetObjectUpgradeLevels(UpgradeableObject upgradeableObject)
+		{
+			try
+			{
+				return this.UpgradeableObjectLevelsField.GetValue(upgradeableObject) as UpgradeableObject.UpgradeLevel[];
+			}
+			catch
+			{
+				return new UpgradeableObject.UpgradeLevel[] { };
+			}
+		}
+
+		public class ConfigurableFacilityDef
+		{
+			private List<ConfigurableLevelDef> levelDefs;
+
+			public string FacilityId
+			{
+				get;
+				private set;
+			}
+
+			public float FacilityMultiplier
+			{
+				get;
+				private set;
+			}
+
+			public IList<ConfigurableLevelDef> LevelDefs
+			{
+				get
+				{
+					return this.levelDefs.AsReadOnly();
+				}
+			}
+
+			private ConfigurableFacilityDef()
+			{
+				this.levelDefs = new List<ConfigurableLevelDef>();
+			}
+
+			public ConfigurableFacilityDef(ConfigNode node) : this()
+			{
+				this.FacilityId = node.GetValue(FACILITY_ID, string.Empty);
+
+				this.FacilityMultiplier = node.GetValue(COST_MULTIPLIER, 1f);
+
+				Tools.PostDebugMessage(this.ToString());
+
+				foreach (ConfigNode levelNode in node.GetNodes(FACILITY_LEVEL))
+				{
+					this.levelDefs.Add(new ConfigurableLevelDef(this, levelNode));
+				}
+			}
+
+			public override string ToString()
+			{
+				return string.Format(
+					"[ConfigurableFacilityDef: FacilityId={0}, FacilityMultiplier={1}]",
+					FacilityId,
+					FacilityMultiplier
+				);
+			}
+		}
+
+		public class ConfigurableLevelDef
+		{
+			private ConfigurableFacilityDef facilityDef;
+
+			public int LevelIdx
+			{
+				get;
+				private set;
+			}
+
+			public float LevelCost
+			{
+				get;
+				private set;
+			}
+
+			public float LevelMultiplier
+			{
+				get;
+				private set;
+			}
+
+			private ConfigurableLevelDef() {}
+
+			public ConfigurableLevelDef(ConfigurableFacilityDef facilityDef, ConfigNode node)
+			{
+				this.facilityDef = facilityDef;
+
+				int levelIdx;
+
+				if (node.TryGetValue(LEVEL_IDX, out levelIdx))
+				{
+					this.LevelIdx = levelIdx;
+				}
+				else
+				{
+					this.LevelIdx = -1;
+				}
+
+				this.LevelCost = node.GetValue(LEVEL_COST, float.NaN);
+
+				this.LevelMultiplier = node.GetValue(COST_MULTIPLIER, 1f);
+
+				if (this.LevelIdx == -1)
+				{
+					Tools.PostErrorMessage("[UpgradeableFacilityConfigurator]" +
+						"LEVEL_IDX not set correctly for level of Facility {0}.  Level will be ignored.\n{1}",
+						this.facilityDef.FacilityId,
+						this.ToString()
+					);
+				}
+
+				if (this.LevelMultiplier != 1f && !float.IsNaN(this.LevelCost))
+				{
+					Tools.PostErrorMessage("[UpgradeableFacilityConfigurator]" +
+						"Multiplier and cost both set for Facility {1} Level {2}: ignoring multiplier definition.",
+						this.facilityDef.FacilityId,
+						this.LevelIdx
+					);
+				}
+
+				Tools.PostDebugMessage(this.ToString());
+			}
+
+			public override string ToString()
+			{
+				return string.Format("[ConfigurableLevelDef: LevelIdx={0}, LevelCost={1}, LevelMultiplier={2}]",
+					LevelIdx,
+					LevelCost,
+					LevelMultiplier
+				);
+			}
+		}
+	}
+}
+
+