internal void debugOutput() { Log.dbg("<debugOutput for {0}>", this.InstanceID); AvailablePart ap = part.partInfo; Log.dbg("prefabCost={0}, dryCost={1}, prefabDryCost={2}", ap.cost, DryCost, (this.scaler.prefab.Modules["TweakScale"] as TweakScale).DryCost); if (this.part.Modules.Contains("ModuleKISItem")) { Log.dbg("kisVolOvr={0}", part.Modules["ModuleKISItem"].Fields["volumeOverride"].GetValue(part.Modules["ModuleKISItem"])); } Log.dbg("ignoreResourcesForCost={0}, ResourceCost={1}", this.ignoreResourcesForCost, (part.Resources.Cast <PartResource>().Aggregate(0.0, (a, b) => a + b.maxAmount * b.info.unitCost))); { TweakScale ts = part.partInfo.partPrefab.Modules ["TweakScale"] as TweakScale; Log.dbg("massFactor={0}", ts.MassFactor); Log.dbg("costFactor={0}", ts.DryCostFactor); Log.dbg("volFactor={0}", ts.VolumeFactor); } Collider x = part.collider; Log.dbg("C: {0}, enabled={1}", x.name, x.enabled); if (part.Modules.Contains("ModuleRCSFX")) { Log.dbg("RCS power={0}", (part.Modules["ModuleRCSFX"] as ModuleRCSFX).thrusterPower); } if (part.Modules.Contains("ModuleEnginesFX")) { Log.dbg("Engine thrust={0}", (part.Modules["ModuleEnginesFX"] as ModuleEnginesFX).maxThrust); } Log.dbg("</debugOutput>"); }
/// <summary> /// Calculate the correct scale to use for scaling a part relative to another. /// </summary> /// <param name="a">Source part, from which we get the desired scale.</param> /// <param name="b">Target part, which will potentially be scaled.</param> /// <returns>The difference in scale between <paramref name="a"/> and <paramref name="b"/>, or null if the parts are incompatible.</returns> private static float?GetRelativeScaling(TweakScale a, TweakScale b) { if (a == null || b == null) { return(null); } var nodes = NodesBetween(a.part, b.part); if (!nodes.HasValue) { return(null); } var nodeA = nodes.Value.Item1; var nodeB = nodes.Value.Item2; if (!a.ScaleType.AttachNodes.ContainsKey(nodeA.id) || !b.ScaleType.AttachNodes.ContainsKey(nodeB.id)) { return(null); } var scaleA = a.ScaleType.AttachNodes[nodeA.id]; var scaleB = b.ScaleType.AttachNodes[nodeB.id]; var baseA = a.ScaleType.BaseScale; var baseB = b.ScaleType.BaseScale; if (scaleA.Family != scaleB.Family) { return(null); } return((scaleA.Scale * baseB) / (scaleB.Scale * baseA)); }
private void OnIncreaseSlotsNumberChange(BaseField field, Object obj) { TweakScale.TweakScale ts_part = this.part.Modules.GetModule <TweakScale.TweakScale>(); ScalingFactor current = ts_part.ScalingFactor; this.OnRescale(current); }
/// <summary> /// Automatically scale part to match other part, if applicable. /// </summary> /// <param name="a">Source part, from which we get the desired scale.</param> /// <param name="b">Target part, which will potentially be scaled.</param> private static void AutoScale(TweakScale a, TweakScale b) { if (a == null || b == null) { return; } if (a.ScaleType != b.ScaleType) { return; } var factor = GetRelativeScaling(a, b); if (!factor.HasValue) { return; } b.tweakScale = b.tweakScale * factor.Value; if (!b.isFreeScale && (b.ScaleFactors.Length > 0)) { b.tweakName = Tools.ClosestIndex(b.tweakScale, b.ScaleFactors); } b.OnTweakScaleChanged(); }
/// <summary> /// Propagate relative scaling factor to children. /// </summary> private void ChainScale() { int len = part.children.Count; for (int i = 0; i < len; i++) { Part child = part.children[i]; TweakScale b = child.GetComponent <TweakScale>(); if (b == null) { continue; } float factor = ScalingFactor.relative.linear; if (Math.Abs(factor - 1) <= 1e-4f) { continue; } b.tweakScale *= factor; if (!b.isFreeScale && (b.ScaleFactors.Length > 0)) { b.tweakName = Tools.ClosestIndex(b.tweakScale, b.ScaleFactors); } b.OnTweakScaleChanged(); } }
private string checkForSanity(Part p) { Log.dbg("Checking Sanity for {0} at {1}", p.name, p.partInfo.partUrl); { TweakScale m = p.Modules.GetModule <TweakScale>(); if (m.Fields["tweakScale"].guiActiveEditor == m.Fields["tweakName"].guiActiveEditor) { return("not being correctly initialized - see issue [#30]( https://github.com/net-lisias-ksp/TweakScale/issues/30 )"); } } if (p.Modules.Contains("ModulePartVariants")) { PartModule m = p.Modules["ModulePartVariants"]; foreach (FieldInfo fi in m.ModuleAttributes.publicFields) { if ("variantList" != fi.Name) { continue; } IList variantList = (IList)fi.GetValue(m); foreach (object partVariant in variantList) { foreach (PropertyInfo property in partVariant.GetType().GetProperties()) { if ("Cost" == property.Name && 0.0 != (float)property.GetValue(partVariant, null)) { return("having a ModulePartVariants with Cost - see issue [#13]( https://github.com/net-lisias-ksp/TweakScale/issues/13 )"); } if ("Mass" == property.Name && 0.0 != (float)property.GetValue(partVariant, null)) { return("having a ModulePartVariants with Mass - see issue [#13]( https://github.com/net-lisias-ksp/TweakScale/issues/13 )"); } } } } } if (p.Modules.Contains("FSbuoyancy")) { return("using FSbuoyancy module - see issue [#9]( https://github.com/net-lisias-ksp/TweakScale/issues/9 )"); } if (p.Modules.Contains("ModuleB9PartSwitch")) { if (p.Modules.Contains("FSfuelSwitch")) { return("having ModuleB9PartSwitch together FSfuelSwitch - see issue [#12]( https://github.com/net-lisias-ksp/TweakScale/issues/12 )"); } if (p.Modules.Contains("ModuleFuelTanks")) { return("having ModuleB9PartSwitch together ModuleFuelTanks - see issue [#12]( https://github.com/net-lisias-ksp/TweakScale/issues/12 )"); } } return(null); }
private string checkForSanity(Part p) { Log.dbg("Checking Sanity for {0} at {1}", p.name, p.partInfo.partUrl); try { TweakScale m = p.Modules.GetModule <TweakScale>(); if (m.active && m.available && m.Fields["tweakScale"].guiActiveEditor == m.Fields["tweakName"].guiActiveEditor) { return("not being correctly initialized - see issue [#30]( https://github.com/net-lisias-ksp/TweakScale/issues/30 )"); } } catch (System.NullReferenceException) { return("having missed attributes - see issue [#30]( https://github.com/net-lisias-ksp/TweakScale/issues/30 )"); } if (p.Modules.Contains("FSbuoyancy") && !p.Modules.Contains("TweakScalerFSbuoyancy")) { return("using FSbuoyancy module without TweakScaleCompanion for Firespitter installed - see issue [#1] from TSC_FS ( https://github.com/net-lisias-ksp/TweakScaleCompantion_FS/issues/1 )"); } if (p.Modules.Contains("ModuleB9PartSwitch")) { if (p.Modules.Contains("FSfuelSwitch")) { return("having ModuleB9PartSwitch together FSfuelSwitch - see issue [#12]( https://github.com/net-lisias-ksp/TweakScale/issues/12 )"); } if (p.Modules.Contains("ModuleFuelTanks")) { return("having ModuleB9PartSwitch together ModuleFuelTanks - see issue [#12]( https://github.com/net-lisias-ksp/TweakScale/issues/12 )"); } } // This would introduce some breakage on already scaling parts on the wild, so I decided to just let it go as it is. // I will get a lot of heat due Mastodon and the Tubes, but at least I will not break current savegames on the wild. :/ #if false if (p.Modules.Contains("ModulePartVariants")) { // It's more straightforward to check the Config Node, as the ModulePart has the Attachment Nodes filled up even // when the Config does not specify them. I'm presuming the ModulePartVariants.variantList[].AttachNodes is being // filled up at runtime to accelerate changing Variants... ConfigNode partNode = GameDatabase.Instance.GetConfigs("PART").FirstOrDefault(c => c.name.Replace('_', '.') == p.name).config; ConfigNode moduleNode = partNode.GetNodes("MODULE").FirstOrDefault(n => n.GetValue("name") == "ModulePartVariants"); ConfigNode[] variantsNodes = moduleNode.GetNodes("VARIANT"); foreach (ConfigNode cn in variantsNodes) { if (null != cn.GetNode("NODES")) { return("having a Variant that change attachment nodes - see issue [#139]( https://github.com/net-lisias-ksp/TweakScale/issues/139 )"); } } } #endif return(null); }
private void InitModule() { this.tweakscale = this.part.Modules.GetModule <TweakScale.TweakScale>(); this.targetPartModule = this.part.Modules.GetModule <FSbuoyancy>(); if (null == this.targetPartModule || !this.targetPartModule.enabled) { this.enabled = false; return; } this.myField = this.Fields["buoyancyPercent"]; this.myUiControl = (this.myField.uiControlEditor as UI_FloatRange); this.myUiControl.onFieldChanged += this.OnMyBuyoancyFieldChange; }
public Implementation(Part part, Listener listener) { this.listener = listener; this.part = part; this.tweakscale = this.part.Modules.GetModule <TweakScale.TweakScale>(); if (null == this.tweakscale) { throw new System.NullReferenceException("TweakScale not found!"); } List <ModuleWaterfallFX> l = this.part.Modules.GetModules <ModuleWaterfallFX>(); if (null == l) { throw new System.NullReferenceException("ModuleWaterfallFX not found!"); } this.targetPartModules = l.ToArray(); }
private void OnEditorAttach() { //only run the following block in the editor; it updates the crew-assignment GUI if (!HighLogic.LoadedSceneIsEditor) { return; } Log.dbg("OnEditorAttach {0}", this.InstanceID); if (null == this.part.parent) { return; // This should be impossible, but better safe than sorry... } TweakScale module = this.part.parent.GetComponent <TweakScale>(); if (null != module && Features.AutoScale.Enabled) { Features.AutoScale.Execute(module, this); } }
/// <summary> /// Calculate the correct scale to use for scaling a part relative to another. /// </summary> /// <param name="a">Source part, from which we get the desired scale.</param> /// <param name="b">Target part, which will potentially be scaled.</param> /// <returns>The difference in scale between the (scaled) attachment nodes connecting <paramref name="a"/> and <paramref name="b"/>, or null if somethinng went wrong.</returns> private static float?GetRelativeScaling(TweakScale a, TweakScale b) { if (a == null || b == null) { return(null); } var nodes = NodesBetween(a.part, b.part); if (!nodes.HasValue) { return(null); } var nodeA = nodes.Value.Item1; var nodeB = nodes.Value.Item2; var aIdx = a._prefabPart.attachNodes.FindIndex(t => t.id == nodeA.id); var bIdx = b._prefabPart.attachNodes.FindIndex(t => t.id == nodeB.id); if (aIdx < 0 || bIdx < 0 || aIdx >= a._prefabPart.attachNodes.Count || aIdx >= a._prefabPart.attachNodes.Count) { return(null); } var sizeA = (float)a._prefabPart.attachNodes[aIdx].size; var sizeB = (float)b._prefabPart.attachNodes[bIdx].size; if (sizeA == 0) { sizeA = 0.5f; } if (sizeB == 0) { sizeB = 0.5f; } return((sizeA * a.tweakScale / a.defaultScale) / (sizeB * b.tweakScale / b.defaultScale)); }
internal void OnRescale(ScalingFactor factor) { Log.dbg("OnRescale {0}:{1:X} to {2}", this.name, this.part.GetInstanceID(), factor.ToString()); KISP.ModuleKISInventory prefab = this.part.partInfo.partPrefab.Modules.GetModule <KISP.ModuleKISInventory>(); KISP.ModuleKISInventory part = this.part.Modules.GetModule <KISP.ModuleKISInventory>(); TweakScale.TweakScale ts_prefab = this.part.partInfo.partPrefab.Modules.GetModule <TweakScale.TweakScale>(); TweakScale.TweakScale ts_part = this.part.Modules.GetModule <TweakScale.TweakScale>(); part.maxVolume = prefab.maxVolume * factor.absolute.cubic; ts_part.DryCost = (float)(ts_prefab.DryCost * factor.absolute.cubic); if (this.increaseSlotsNumber) { //part.slotSize = prefab.slotSize; part.slotsX = (int)Math.Floor(prefab.slotsX * factor.absolute.linear); part.slotsY = (int)Math.Floor(prefab.slotsY * factor.absolute.linear); int slotsCount = part.slotsX * part.slotsY; if (slotsCount > prefab.slotsX * prefab.slotsY) { Log.dbg("before {0} {1}", part.maxVolume, ts_part.DryCost); part.maxVolume -= (float)(slotsCount * (0.0005 * part.maxVolume)); // Reduce volume by 0.05% per slot ts_part.DryCost += (float)(slotsCount * (0.001 * ts_part.DryCost)); // Add 0.1% of cost penalty per slot Log.dbg("after {0} {1}", part.maxVolume, ts_part.DryCost); } } else { //part.slotSize = (int)Math.Floor(prefab.slotSize * factor.absolute.linear); part.slotsX = prefab.slotsX; part.slotsY = prefab.slotsY; } // FIXME: Resize the Inventory Window size! Log.dbg("Current size : {0} maxVolume, {1} slotsX, {2} slotsX, {3} dry cost; {4} currentScale; {5} defaultScale", part.maxVolume, part.slotsX, part.slotsY, ts_part.DryCost, ts_part.currentScale, ts_part.defaultScale); }
/// <summary> /// Calculate the correct scale to use for scaling a part relative to another. /// </summary> /// <param name="a">Source part, from which we get the desired scale.</param> /// <param name="b">Target part, which will potentially be scaled.</param> /// <returns>The difference in scale between the (scaled) attachment nodes connecting <paramref name="a"/> and <paramref name="b"/>, or null if somethinng went wrong.</returns> private static float? GetRelativeScaling(TweakScale a, TweakScale b) { if (a == null || b == null) return null; var nodes = NodesBetween(a.part, b.part); if (!nodes.HasValue) return null; var nodeA = nodes.Value.Item1; var nodeB = nodes.Value.Item2; var aIdx = a._prefabPart.attachNodes.FindIndex( t => t.id == nodeA.id); var bIdx = b._prefabPart.attachNodes.FindIndex( t => t.id == nodeB.id); if (aIdx < 0 || bIdx < 0 || aIdx >= a._prefabPart.attachNodes.Count || aIdx >= a._prefabPart.attachNodes.Count) return null; var sizeA = (float)a._prefabPart.attachNodes[aIdx].size; var sizeB = (float)b._prefabPart.attachNodes[bIdx].size; if (sizeA == 0) sizeA = 0.5f; if (sizeB == 0) sizeB = 0.5f; return (sizeA * a.tweakScale/a.defaultScale)/(sizeB * b.tweakScale/b.defaultScale); }
public EmitterUpdater(Part part) { _part = part; _ts = part.Modules.OfType <TweakScale>().First(); }
/// <summary> /// Automatically scale part to match other part, if applicable. /// </summary> /// <param name="a">Source part, from which we get the desired scale.</param> /// <param name="b">Target part, which will potentially be scaled.</param> private static void AutoScale(TweakScale a, TweakScale b) { if (a == null || b == null) return; var factor = GetRelativeScaling(a,b); if (!factor.HasValue) return; b.tweakScale = a.tweakScale * factor.Value; if (a.ScaleFactors.Length > 0) { b.tweakName = Tools.ClosestIndex(b.tweakScale, b.ScaleFactors); } b.OnTweakScaleChanged(); }
/// <summary> /// Calculate the correct scale to use for scaling a part relative to another. /// </summary> /// <param name="a">Source part, from which we get the desired scale.</param> /// <param name="b">Target part, which will potentially be scaled.</param> /// <returns>The difference in scale between <paramref name="a"/> and <paramref name="b"/>, or null if the parts are incompatible.</returns> private static float? GetRelativeScaling(TweakScale a, TweakScale b) { if (a == null || b == null) return null; var nodes = NodesBetween(a.part, b.part); if (!nodes.HasValue) return null; var nodeA = nodes.Value.Item1; var nodeB = nodes.Value.Item2; if (!a.ScaleType.AttachNodes.ContainsKey(nodeA.id) || !b.ScaleType.AttachNodes.ContainsKey(nodeB.id)) return null; var scaleA = a.ScaleType.AttachNodes[nodeA.id]; var scaleB = b.ScaleType.AttachNodes[nodeB.id]; var baseA = a.ScaleType.BaseScale; var baseB = b.ScaleType.BaseScale; if (scaleA.Family != scaleB.Family) return null; return (scaleA.Scale*baseB)/(scaleB.Scale*baseA); }
private IEnumerator WriteDryCost() { PrefabDryCostWriter.isConcluded = false; Debug.Log("TweakScale::WriteDryCost: Started"); for (int i = WAIT_ROUNDS; i >= 0 && null == PartLoader.LoadedPartsList; --i) { yield return(null); if (0 == i) { Debug.LogError("TweakScale::Timeout waiting for PartLoader.LoadedPartsList!!"); } } // I Don't know if this is needed, but since I don't know that this is not needed, // I choose to be safe than sorry! { int last_count = int.MinValue; for (int i = WAIT_ROUNDS; i >= 0; --i) { if (last_count == PartLoader.LoadedPartsList.Count) { break; } last_count = PartLoader.LoadedPartsList.Count; yield return(null); if (0 == i) { Debug.LogError("TweakScale::Timeout waiting for PartLoader.LoadedPartsList.Count!!"); } } } foreach (AvailablePart p in PartLoader.LoadedPartsList) { for (int i = WAIT_ROUNDS; i >= 0 && null == p.partPrefab && null == p.partPrefab.Modules && p.partPrefab.Modules.Count < 1; --i) { yield return(null); if (0 == i) { Debug.LogErrorFormat("TweakScale::Timeout waiting for {0}.prefab.Modules!!", p.name); } } Part prefab = p.partPrefab; // Historically, we had problems here. // However, that co-routine stunt appears to have solved it. // But we will keep this as a ghinea-pig in the case the problem happens again. try { if (!prefab.Modules.Contains("TweakScale")) { continue; } } catch (Exception e) { Debug.LogErrorFormat("[TweakScale] Exception on {0}.prefab.Modules.Contains: {1}", p.name, e); Debug.LogWarningFormat("{0}", prefab.Modules); continue; // TODO: Cook a way to try again! } { string r = this.checkForSanity(prefab); if (null != r) { // There are some known situations where TweakScale is capsizing. If such situations are detected, we just // refuse to scale it. Sorry. Debug.LogWarningFormat("[TweakScale] Removing TweakScale support for {0}.", p.name); prefab.Modules.Remove(prefab.Modules["TweakScale"]); Debug.LogErrorFormat("[TweakScale] Part {0} didn't passed the sanity check due {1}.", p.name, r); continue; } } try { TweakScale m = prefab.Modules["TweakScale"] as TweakScale; m.DryCost = (float)(p.cost - prefab.Resources.Cast <PartResource>().Aggregate(0.0, (a, b) => a + b.maxAmount * b.info.unitCost)); m.ignoreResourcesForCost |= prefab.Modules.Contains("FSfuelSwitch"); if (m.DryCost < 0) { Debug.LogErrorFormat("TweakScale::PrefabDryCostWriter: negative dryCost: part={0}, DryCost={1}", p.name, m.DryCost); m.DryCost = 0; } #if DEBUG Debug.LogFormat("Part {0} has drycost {1} with ignoreResourcesForCost {2}", p.name, m.DryCost, m.ignoreResourcesForCost); #endif } catch (Exception e) { Debug.LogErrorFormat("[TweakScale] part={0} ({1}) Exception on writeDryCost: {2}", p.name, p.title, e); } } Debug.Log("TweakScale::WriteDryCost: Concluded"); PrefabDryCostWriter.isConcluded = true; }
public EmitterUpdater(Part part) { _part = part; _basePart = PartLoader.getPartInfoByName(part.partInfo.name).partPrefab; _ts = part.Modules.OfType <TweakScale>().First(); }
private IEnumerator WriteDryCost() { PrefabDryCostWriter.isConcluded = false; Debug.Log("TweakScale::WriteDryCost: Started"); { // Toe Stomping Fest prevention for (int i = WAIT_ROUNDS; i >= 0 && null == PartLoader.LoadedPartsList; --i) { yield return(null); if (0 == i) { Debug.LogError("TweakScale::Timeout waiting for PartLoader.LoadedPartsList!!"); } } // I Don't know if this is needed, but since I don't know that this is not needed, // I choose to be safe than sorry! { int last_count = int.MinValue; for (int i = WAIT_ROUNDS; i >= 0; --i) { if (last_count == PartLoader.LoadedPartsList.Count) { break; } last_count = PartLoader.LoadedPartsList.Count; yield return(null); if (0 == i) { Debug.LogError("TweakScale::Timeout waiting for PartLoader.LoadedPartsList.Count!!"); } } } } int sanity_failures = 0; int showstoppers_failures = 0; foreach (AvailablePart p in PartLoader.LoadedPartsList) { for (int i = WAIT_ROUNDS; i >= 0 && null == p.partPrefab && null == p.partPrefab.Modules && p.partPrefab.Modules.Count < 1; --i) { yield return(null); if (0 == i) { Debug.LogErrorFormat("TweakScale::Timeout waiting for {0}.prefab.Modules!!", p.name); } } Part prefab; { // Historically, we had problems here. // However, that co-routine stunt appears to have solved it. // But we will keep this as a ghinea-pig in the case the problem happens again. int retries = WAIT_ROUNDS; bool containsTweakScale = false; Exception culprit = null; prefab = p.partPrefab; // Reaching the prefab here in the case another Mod recreates it from zero. If such hypothecical mod recreates the whole part, we're doomed no matter what. while (retries > 0) { bool should_yield = false; try { containsTweakScale = prefab.Modules.Contains("TweakScale"); // Yeah. This while stunt was done just due this. All the rest is plain clutter! :D break; } catch (Exception e) { culprit = e; --retries; should_yield = true; } if (should_yield) // This stunt is needed as we can't yield from inside a try-catch! { yield return(null); } } if (0 == retries) { Debug.LogErrorFormat("[TweakScale] Exception on {0}.prefab.Modules.Contains: {1}", p.name, culprit); Debug.LogWarningFormat("{0}", prefab.Modules); continue; } if (!containsTweakScale) { continue; } // End of hack. Ugly, uh? :P } #if DEBUG { Debug.LogFormat("Found part named {0}. title {1}:", p.name, p.title); foreach (PartModule m in prefab.Modules) { Debug.LogFormat("\tPart {0} has module {1}", p.name, m.moduleName); } } #endif try { string r = null; // We check for fixable problems first, in the hope to prevent by luck a ShowStopper below. if (null != (r = this.checkForSanity(prefab))) { // There are some known situations where TweakScale is capsizing. If such situations are detected, we just // refuse to scale it. Sorry. Debug.LogWarningFormat("[TweakScale] Removing TweakScale support for {0}.", p.name); prefab.Modules.Remove(prefab.Modules["TweakScale"]); Debug.LogErrorFormat("[TweakScale] Part {0} didn't passed the sanity check due {1}.", p.name, r); ++sanity_failures; continue; } if (null != (r = this.checkForShowStoppers(prefab))) { // This are situations that we should not allow the KSP to run to prevent serious corruption. // This is **FAR** from a good measure, but it's the only viable. Debug.LogWarningFormat("[TweakScale] **FATAL** Found a showstopper problem on {0}.", p.name); prefab.Modules.Remove(prefab.Modules["TweakScale"]); Debug.LogErrorFormat("[TweakScale] **FATAL** Part {0} has a fatal problem due {1}.", p.name, r); ++showstoppers_failures; continue; } } catch (Exception e) { Debug.LogErrorFormat("[TweakScale] part={0} ({1}) Exception on Sanity Checks: {2}", p.name, p.title, e); } try { TweakScale m = prefab.Modules["TweakScale"] as TweakScale; m.DryCost = (float)(p.cost - prefab.Resources.Cast <PartResource>().Aggregate(0.0, (a, b) => a + b.maxAmount * b.info.unitCost)); m.ignoreResourcesForCost |= prefab.Modules.Contains("FSfuelSwitch"); if (m.DryCost < 0) { Debug.LogErrorFormat("TweakScale::PrefabDryCostWriter: negative dryCost: part={0}, DryCost={1}", p.name, m.DryCost); m.DryCost = 0; } #if DEBUG Debug.LogFormat("Part {0} has drycost {1} with ignoreResourcesForCost {2}", p.name, m.DryCost, m.ignoreResourcesForCost); #endif } catch (Exception e) { Debug.LogErrorFormat("[TweakScale] part={0} ({1}) Exception on writeDryCost: {2}", p.name, p.title, e); } } Debug.Log("TweakScale::WriteDryCost: Concluded"); PrefabDryCostWriter.isConcluded = true; if (showstoppers_failures > 0) { GUI.ShowStopperAlertBox.Show(showstoppers_failures); } else if (sanity_failures > 0) { GUI.SanityCheckAlertBox.show(sanity_failures); } }
private IEnumerator WriteDryCost() { PrefabDryCostWriter.isConcluded = false; Log.info("WriteDryCost: Started"); { // Toe Stomping Fest prevention for (int i = WAIT_ROUNDS; i >= 0 && null == PartLoader.LoadedPartsList; --i) { yield return(null); if (0 == i) { Log.warn("Timeout waiting for PartLoader.LoadedPartsList!!"); } } // I Don't know if this is needed, but since I don't know that this is not needed, // I choose to be safe than sorry! { int last_count = int.MinValue; for (int i = WAIT_ROUNDS; i >= 0; --i) { if (last_count == PartLoader.LoadedPartsList.Count) { break; } last_count = PartLoader.LoadedPartsList.Count; yield return(null); if (0 == i) { Log.warn("Timeout waiting for PartLoader.LoadedPartsList.Count!!"); } } } } int check_failures = 0; int sanity_failures = 0; int showstoppers_failures = 0; int check_overrulled = 0; foreach (AvailablePart p in PartLoader.LoadedPartsList) { for (int i = WAIT_ROUNDS; i >= 0 && (null == p.partPrefab || null == p.partPrefab.Modules); --i) { yield return(null); if (0 == i) { Log.error("Timeout waiting for {0}.prefab.Modules!!", p.name); } } Part prefab; { // Historically, we had problems here. // However, that co-routine stunt appears to have solved it. // But we will keep this as a ghinea-pig in the case the problem happens again. int retries = WAIT_ROUNDS; bool containsTweakScale = false; Exception culprit = null; prefab = p.partPrefab; // Reaching the prefab here in the case another Mod recreates it from zero. If such hypothecical mod recreates the whole part, we're doomed no matter what. while (retries > 0) { bool should_yield = false; try { containsTweakScale = prefab.Modules.Contains("TweakScale"); // Yeah. This while stunt was done just to be able to do this. All the rest is plain clutter! :D break; } catch (Exception e) { culprit = e; --retries; should_yield = true; } if (should_yield) // This stunt is needed as we can't yield from inside a try-catch! { yield return(null); } } if (0 == retries) { Log.error("Exception on {0}.prefab.Modules.Contains: {1}", p.name, culprit); Log.detail("{0}", prefab.Modules); continue; } if (!containsTweakScale) { Log.dbg("The part named {0} ; title {1} doesn't supports TweakScale. Skipping.", p.name, p.title); continue; } // End of hack. Ugly, uh? :P } #if DEBUG { Log.dbg("Found part named {0} ; title {1}:", p.name, p.title); foreach (PartModule m in prefab.Modules) { Log.dbg("\tPart {0} has module {1}", p.name, m.moduleName); } } #endif try { string r = null; // We check for fixable problems first, in the hope to prevent by luck a ShowStopper below. // These Offending Parts never worked before, or always ends in crashing KSP, so the less worse // line of action is to remove TweakScale from them in order to allow the player to at least keep // playing KSP. Current savegames can break, but they were going to crash and loose everything anyway!! if (null != (r = this.checkForSanity(prefab))) { // There are some known situations where TweakScale is capsizing. If such situations are detected, we just // refuse to scale it. Sorry. Log.warn("Removing TweakScale support for {0} ({1}).", p.name, p.title); prefab.Modules.Remove(prefab.Modules["TweakScale"]); Log.error("Part {0} ({1}) didn't passed the sanity check due {2}.", p.name, p.title, r); ++sanity_failures; continue; } // This one is for my patches that "break things again" in a controlled way to salvage already running savegames // that would be lost by fixing things right. Sometimes, it's possible to keep the badly patched parts ongoing, as // as the nastiness will not crash KSP (besides still corrupting savegames and craft files in a way that would not // allow the user to share them). // Since we are overruling the checks, we abort the remaining ones. Yes, this allows abuse, but whatever... I can't // save the World, just the savegames. :) if (null != (r = this.checkForOverules(prefab))) { // This is for detect and log the Breaking Parts patches. // See issue [#56]( https://github.com/net-lisias-ksp/TweakScale/issues/56 ) for details. // This is **FAR** from a good measure, but it's the only viable. Log.warn("Part {0} ({1}) has the issue(s) overrule(s) {2}. See [#56]( https://github.com/net-lisias-ksp/TweakScale/issues/56 ) for details.", p.name, p.title, r); ++check_overrulled; } // And now we check for the ShowStoppers. // These ones happens due rogue patches, added after a good installment could starts savegames, what ends up corrupting them! // Since we don't have how to know when this happens, and since originally the part was working fine, we don't know // how to proceeed. So the only sensible option is to scare the user enough to make him/her go to the Forum for help // so we can identify the offending patch and then provide a solution that would preserve his savegame. // We also stops any further processing, as we could damage something that is already damaged. else if (null != (r = this.checkForShowStoppers(prefab))) { // This are situations that we should not allow the KSP to run to prevent serious corruption. // This is **FAR** from a good measure, but it's the only viable. Log.warn("**FATAL** Found a showstopper problem on {0} ({1}).", p.name, p.title); Log.error("**FATAL** Part {0} ({1}) has a fatal problem due {2}.", p.name, p.title, r); ++showstoppers_failures; continue; } } catch (Exception e) { ++check_failures; Log.error("part={0} ({1}) Exception on Sanity Checks: {2}", p.name, p.title, e); } // If we got here, the part is good to go, or was overulled into a sane configuration that would allow us to proceed. try { TweakScale m = prefab.Modules["TweakScale"] as TweakScale; m.DryCost = (float)(p.cost - prefab.Resources.Cast <PartResource>().Aggregate(0.0, (a, b) => a + b.maxAmount * b.info.unitCost)); m.ignoreResourcesForCost |= prefab.Modules.Contains("FSfuelSwitch"); if (m.DryCost < 0) { Log.error("PrefabDryCostWriter: negative dryCost: part={0}, DryCost={1}", p.name, m.DryCost); m.DryCost = 0; } Log.dbg("Part {0} ({1}) has drycost {2} with ignoreResourcesForCost {3}", p.name, p.title, m.DryCost, m.ignoreResourcesForCost); } catch (Exception e) { ++check_failures; Log.error("part={0} ({1}) Exception on writeDryCost: {2}", p.name, p.title, e); } } Log.info("TweakScale::WriteDryCost: Concluded : {0} checks failed ; {1} parts with issues overruled ; {2} Show Stoppers found; {3} Sanity Check failed;", check_failures, check_overrulled, showstoppers_failures, sanity_failures); PrefabDryCostWriter.isConcluded = true; if (showstoppers_failures > 0) { GUI.ShowStopperAlertBox.Show(showstoppers_failures); } else { if (check_overrulled > 0) { GUI.OverrulledAdviseBox.show(check_overrulled); } if (sanity_failures > 0) { GUI.SanityCheckAlertBox.show(sanity_failures); } if (check_failures > 0) { GUI.CheckFailureAlertBox.show(check_failures); } } }
public EmitterUpdater(Part part) { _part = part; _basePart = PartLoader.getPartInfoByName(part.partInfo.name).partPrefab; _ts = part.Modules.OfType<TweakScale>().First(); }