Inheritance: PartModule, IPartCostModifier
Example #1
0
        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>");
        }
Example #2
0
        /// <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);
        }
Example #4
0
        /// <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();
        }
Example #5
0
        /// <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();
        }
Example #10
0
        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);
            }
        }
Example #11
0
        /// <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);
        }
Example #13
0
        /// <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);
        }
Example #14
0
 public EmitterUpdater(Part part)
 {
     _part = part;
     _ts   = part.Modules.OfType <TweakScale>().First();
 }
Example #15
0
        /// <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();
        }
Example #16
0
        /// <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;
        }
Example #18
0
 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);
                }
            }
        }
Example #21
0
 public EmitterUpdater(Part part)
 {
     _part = part;
     _basePart = PartLoader.getPartInfoByName(part.partInfo.name).partPrefab;
     _ts = part.Modules.OfType<TweakScale>().First();
 }