Beispiel #1
0
            public IEnumerable <DialogGUIBase> GetTraitDescription(bool showParts, List <ProtoCrewMember> crew)
            {
                List <ExperienceTraitConfig> careers = GameDatabase.Instance.ExperienceConfigs
                                                       .GetTraitsWithEffect(this.Trait)
                                                       .Select(name => GameDatabase.Instance.ExperienceConfigs.GetExperienceTraitConfig(name))
                                                       .ToList();

                //  #Needed    Specialist   #Crew    Generalist   #Crew
                //  1.5        Miner        2        3*Engineer   0
                //    Scrounger Drill-

                var requirementRow = new DialogGUIHorizontalLayout();

                string quantity = UncrewedQuantity + DisabledQuantity > 0.001
                    ? $"{this.TotalQuantity - UncrewedQuantity - DisabledQuantity:N1}/{this.TotalQuantity:N1}"
                    : this.TotalQuantity.ToString("N1");

                if (UncrewedQuantity > 0.001)
                {
                    quantity = TextEffects.Red(quantity);
                }
                else if (DisabledQuantity > 0.001)
                {
                    quantity = TextEffects.Yellow(quantity);
                }
                requirementRow.AddChild(new DialogGUILabel(quantity, width: NumberColumnWidth));
                for (int i = 0; i < careers.Count; ++i)
                {
                    ExperienceEffectConfig effectConfig = careers[i].Effects.First(effect => effect.Name == this.Trait);
                    var levelString = effectConfig.Config.GetValue("level");
                    int numStars    = SkillLevel - int.Parse(levelString ?? "0");

                    requirementRow.AddChild(new DialogGUILabel(
                                                PksCrewRequirement.DescribeKerbalTrait(numStars, careers[i].Title), ProfessionColumnWidth));
                    requirementRow.AddChild(new DialogGUILabel(
                                                $"{crew.Count(c => c.trait == careers[i].Title && c.experienceLevel >= numStars)}",
                                                width: NumberColumnWidth));
                }
                yield return(requirementRow);

                if (showParts)
                {
                    var partsRow = new DialogGUIHorizontalLayout();
                    partsRow.AddChild(new DialogGUISpace(15));
                    partsRow.AddChild(new DialogGUILabel($"<I>{string.Join(", ", this.PartNames)}</I>"));
                    yield return(partsRow);
                }
            }
        public override string GetInfo()
        {
            StringBuilder info = new StringBuilder();

            if (this.Input != null)
            {
                info.AppendLine($"{TextEffects.Green("Input:")} {this.Input.BaseName}");
            }

            info.AppendLine($"{TextEffects.Green("Capacity:")} {this.capacity} {this.Output.CapacityUnits}");
            info.AppendLine($"{TextEffects.Green("Output:")} {this.untieredOutput}");

            info.AppendLine($"{TextEffects.Green("%Local:")}");
            foreach (TechTier tier in TechTierExtensions.AllTiers)
            {
                info.AppendLine($" {tier.ToString()}: {(int)(combinationRates[(int)tier] * 100)}%");
            }

            return(info.ToString());
        }
        public override string GetInfo()
        {
            List <ExperienceTraitConfig> careers = GameDatabase.Instance.ExperienceConfigs
                                                   .GetTraitsWithEffect(this.requiredEffect)
                                                   .Select(name => GameDatabase.Instance.ExperienceConfigs.GetExperienceTraitConfig(name))
                                                   .ToList();

            StringBuilder info = new StringBuilder();

            info.AppendLine(TextEffects.Green("Required Crew:"));
            info.AppendLine($"Staffing Level: {this.requiredCrew}");
            info.AppendLine(TextEffects.Green("Traits:"));
            foreach (TechTier tier in TechTierExtensions.AllTiers)
            {
                info.Append($"{tier}: ");

                info.Append(DescribeKerbalsWithEffect(this.requiredEffect, tier));
                info.AppendLine();
            }
            return(info.ToString());
        }
Beispiel #4
0
 private DialogGUIBase DrawProductionTab()
 {
     return(new DialogGUIVerticalLayout(
                new DialogGUILabel(() => this.introLineMessage),
                new DialogGUISpace(5),
                new DialogGUIHorizontalLayout(
                    new DialogGUILabel(
                        () => $"{TextEffects.DialogHeading("Production/day:")}\r\n{this.productionMessage ?? "<none>"}"),
                    new DialogGUISpace(30),
                    new DialogGUILabel(() => this.unusedCapacityMessage == null
                 ? ""
                 : $"{TextEffects.DialogHeading("Unused Capacity:")}\r\n{this.unusedCapacityMessage}")),
                new DialogGUISpace(5),
                new DialogGUILabel(() => this.limitedByMessage == null
             ? ""
             : $"{TextEffects.DialogHeading("Limited by:")} {this.limitedByMessage}"),
                new DialogGUIFlexibleSpace(),
                new DialogGUIHorizontalLayout(TextAnchor.MiddleLeft,
                                              new DialogGUILabel("What if we"),
                                              new DialogGUIButton("Add", () => { ++crewDelta; }, () => true, false),
                                              new DialogGUILabel("/"),
                                              new DialogGUIButton("Remove", () => { --crewDelta; }, () => FlightGlobals.ActiveVessel.GetCrewCount() + this.crewDelta > 1, false),
                                              new DialogGUILabel("a kerbal?"))));
 }
Beispiel #5
0
        internal static void BuildStatusStrings(
            bool isAutoMining,
            string minerStatusMessage,
            bool anyCrewDeficiencies,
            bool anyDisabledParts,
            SnackConsumption activeSnackConsumption,
            Dictionary <string, double> resources,
            Dictionary <string, double> storage,
            List <ITieredProducer> tieredProducers,
            List <ITieredCombiner> tieredCombiners,
            int crewCount,
            int crewDelta,
            out string introLineMessage,
            out string productionMessage,
            out string unusedCapacityMessage,
            out string limitedByMessage,
            out List <ResearchData> progress)
        {
            ResearchSink researchSink = new ResearchSink();

            TieredProduction.CalculateResourceUtilization(
                crewCount + crewDelta, 1, tieredProducers, tieredCombiners, researchSink, resources, storage,
                out double timePassed, out var _, out Dictionary <string, double> resourcesConsumed,
                out Dictionary <string, double> resourcesProduced,
                out IEnumerable <string> limitingResources,
                out Dictionary <string, double> unusedProduction);

            if (timePassed == 0)
            {
                var introMessageBuilder = new StringBuilder();

                if (!activeSnackConsumption.IsAtHome)
                {
                    Dictionary <int, List <ProtoCrewMember> > buckets = new Dictionary <int, List <ProtoCrewMember> >();
                    foreach (var crew in activeSnackConsumption.Vessel.GetVesselCrew())
                    {
                        var kerbalIsKnown = LifeSupportScenario.Instance.TryGetStatus(crew, out double daysSinceMeal, out double daysToGrouchy, out bool isGrouchy);
                        if (!kerbalIsKnown)
                        {
                            // Maybe if ! on kerban we complain about this?
                            // Debug.LogError($"Couldn't find a life support record for {crew.name}");
                        }

                        int bucketKey = isGrouchy ? -1 : (int)daysToGrouchy;
                        if (!buckets.TryGetValue(bucketKey, out var crewInBucket))
                        {
                            crewInBucket = new List <ProtoCrewMember>();
                            buckets.Add(bucketKey, crewInBucket);
                        }
                        crewInBucket.Add(crew);
                    }

                    CrewBlurbs.random = new System.Random(FlightGlobals.ActiveVessel.GetHashCode());
                    foreach (List <ProtoCrewMember> crewInBucket in buckets.Values)
                    {
                        // yeah yeah, recomputing this is wasteful & all...
                        LifeSupportScenario.Instance.TryGetStatus(crewInBucket[0], out double daysSinceMeal, out double daysToGrouchy, out bool isGrouchy);
                        if (isGrouchy)
                        {
                            introMessageBuilder.AppendLine(TextEffects.Red(CrewBlurbs.StarvingKerbals(crewInBucket)));
                        }
                        else if (daysToGrouchy < 2)
                        {
                            introMessageBuilder.AppendLine(CrewBlurbs.GrumpyKerbals(crewInBucket, daysToGrouchy, tieredProducers.Any()));
                        }
                        else
                        {
                            introMessageBuilder.AppendLine(CrewBlurbs.HungryKerbals(crewInBucket, daysToGrouchy, tieredProducers.Any()));
                        }
                    }

                    if (tieredProducers.Any())
                    {
                        introMessageBuilder.AppendLine();
                        introMessageBuilder.AppendLine("Nothing can be produced at this base until the snack situation gets fixed.");
                    }
                }

                introLineMessage      = introMessageBuilder.ToString();
                productionMessage     = null;
                unusedCapacityMessage = null;
                limitedByMessage      = null;
                progress = new List <ResearchData>();
            }
            else if (crewCount == 0)
            {
                introLineMessage      = "No kerbals are aboard to produce anything.";
                productionMessage     = null;
                unusedCapacityMessage = null;
                limitedByMessage      = null;
                progress = new List <ResearchData>();
            }
            else
            {
                if (resourcesConsumed.Any())
                {
                    var consumptionBuilder = new StringBuilder();
                    if (minerStatusMessage != null)
                    {
                        consumptionBuilder.AppendLine(minerStatusMessage);
                        consumptionBuilder.AppendLine();
                    }

                    consumptionBuilder.AppendLine(TextEffects.DialogHeading(crewDelta == 0
                        ? $"The crew of {crewCount + crewDelta} is using:"
                        : $"A crew of {crewCount + crewDelta} would use:"));
                    foreach (var resourceName in resourcesConsumed.Keys.OrderBy(n => n))
                    {
                        if (!isAutoMining || !IsCrushinResource(researchSink, resourceName))
                        {
                            double perDay   = TieredProduction.UnitsPerSecondToUnitsPerDay(resourcesConsumed[resourceName]);
                            double daysLeft = resources[resourceName] / perDay;
                            consumptionBuilder.AppendLine($"{perDay:N1} {resourceName} per day ({daysLeft:N1} days left)");
                        }
                    }

                    introLineMessage = consumptionBuilder.ToString();
                }
                else
                {
                    introLineMessage = $"This vessel can sustain a crew of {crewCount + crewDelta} indefinitely.";
                }

                if (resourcesProduced != null && resourcesProduced.Count > 0)
                {
                    var productionMessageBuilder = new StringBuilder();
                    foreach (var resourceName in resourcesProduced.Keys.OrderBy(n => n))
                    {
                        double perDay = TieredProduction.UnitsPerSecondToUnitsPerDay(resourcesProduced[resourceName]);
                        productionMessageBuilder.AppendLine($"{perDay:N1} {resourceName}");
                    }

                    productionMessage = productionMessageBuilder.ToString();
                }
                else
                {
                    productionMessage = null;
                }

                // Because of the way PksTieredCombiners work, we'll often end up with the non-tiered stuff
                // showing up as a rate-limiter.  While it's technically correct, it's not going to be a thing
                // that the player will want to know about.
                var localParts = ColonizationResearchScenario.Instance.TryGetTieredResourceByName("LocalParts");
                if (localParts != null)
                {
                    foreach (TechTier t in Enum.GetValues(typeof(TechTier)))
                    {
                        unusedProduction.Remove(localParts.TieredName(t));
                    }
                }

                unusedCapacityMessage = unusedProduction.Any()
                    ? string.Join("\r\n", unusedProduction.Select(pair => $"{pair.Value:N1} {pair.Key}").ToArray())
                    : null;

                List <string> shortfalls = new List <string>();
                if (anyCrewDeficiencies)
                {
                    shortfalls.Add("uncrewed parts");
                }
                if (anyDisabledParts)
                {
                    shortfalls.Add("disabled parts");
                }
                shortfalls.AddRange(limitingResources);
                shortfalls.AddRange(tieredCombiners
                                    .Where(tc => tc.IsProductionEnabled)
                                    .Select(tc => tc.NonTieredOutputResourceName)
                                    .Where(resourceName => !storage.ContainsKey(resourceName))
                                    .Distinct()
                                    .Select(resourceName => $"storage for {resourceName}"));
                shortfalls.AddRange(tieredProducers
                                    .Where(tp => tp.IsProductionEnabled && tp.Output.CanBeStored)
                                    .Select(tp => tp.Output.TieredName(tp.Tier))
                                    .Where(resourceName => !storage.ContainsKey(resourceName))
                                    .Distinct()
                                    .Select(resourceName => $"storage for {resourceName}"));

                limitedByMessage = shortfalls.Count == 0 ? null : string.Join(", ", shortfalls.ToArray());

                var allResearchEntries = researchSink.Data.Values.ToList();
                foreach (var group in tieredProducers
                         .Where(tp => !researchSink.Data.ContainsKey(tp.Output.ResearchCategory))
                         .GroupBy(tp => tp.Output.ResearchCategory))
                {
                    var maxTier          = group.Max(tp => tp.Tier);
                    var topTierProducers = group.Where(tp => tp.Tier == maxTier).ToArray();

                    ITieredProducer exampleProducer = group.FirstOrDefault(tp => tp.IsResearchEnabled && tp.IsProductionEnabled);
                    // We're looking for the best example of why research isn't enabled - maxtier is the top
                    exampleProducer = topTierProducers.FirstOrDefault(tp => tp.Tier == TechTier.Tier4);
                    if (exampleProducer == null)
                    {
                        exampleProducer = topTierProducers.FirstOrDefault(tp => tp.IsProductionEnabled);
                    }

                    if (exampleProducer == null)
                    {
                        exampleProducer = topTierProducers.First();
                    }

                    var researchEntry = ColonizationResearchScenario.Instance.GetResearchProgress(
                        exampleProducer.Output, exampleProducer.Body, exampleProducer.Tier,
                        exampleProducer.IsResearchEnabled ? "Production Blocked" : exampleProducer.ReasonWhyResearchIsDisabled);
                    allResearchEntries.Add(researchEntry);
                }

                progress = allResearchEntries;
            }
        }
Beispiel #6
0
        private static string MakePartCountString(string partName, IEnumerable <PksCrewRequirement> parts)
        {
            var partsArray   = parts.ToArray();
            int numUncrewed  = parts.Count(p => p.IsRunning && !p.IsStaffed);
            int numTurnedOff = parts.Count(p => !p.IsRunning);

            if (numUncrewed == 0 && numTurnedOff == 0)
            {
                return(MakePartAndCountString(partsArray.Length, partName));
            }
            else if (numUncrewed > 0 && numTurnedOff == 0)
            {
                return($"{MakePartAndCountString(partsArray.Length - numUncrewed, partName)} {TextEffects.Red($"({numUncrewed} unstaffed)")}");
            }
            else if (numUncrewed == 0 && numTurnedOff == partsArray.Length)
            {
                return(TextEffects.Yellow($"{MakePartAndCountString(numTurnedOff, partName)} (disabled)"));
            }
            else if (numUncrewed == 0 && numTurnedOff > 0)
            {
                return($"{MakePartAndCountString(partsArray.Length - numTurnedOff, partName)} {TextEffects.Yellow($"({numTurnedOff} disabled)")}");
            }
            else
            {
                return($"{MakePartAndCountString(partsArray.Length - numUncrewed - numTurnedOff, partName)} {TextEffects.Red($"({numUncrewed} unstaffed, {numTurnedOff} disabled)")}");
            }
        }
        private DialogGUIBase DrawCalculatorDialog()
        {
            List <Part>            parts     = EditorLogic.fetch.ship.Parts;
            List <ITieredProducer> producers = parts
                                               .Select(p => p.FindModuleImplementing <ITieredProducer>())
                                               .Where(p => p != null).ToList();

            if (!producers.Any(p => p.Output.IsEdible && p.Tier == TechTier.Tier4 && p.Output.GetPercentOfDietByTier(TechTier.Tier4) == 1) &&
                !parts.Any(p => p.Resources.Any(r => r.resourceName == "Snacks-Tier4" && r.amount > 0)))
            {
                return(new DialogGUILabel("There is no source of top-tier Snacks on this vessel - only well-fed and happy Kerbals will produce things"));
            }

            // If we have some LandedOnBody and some not, that's going to spell trouble for our ability to calculate anything sensible.
            if (producers.Select(p => p.Output.ProductionRestriction == ProductionRestriction.LandedOnBody).Distinct().Count() > 1)
            {
                return(new DialogGUILabel("This ship looks like a composite ship - that is one with several sub-ships to take on "
                                          + "different missions (e.g. landed, in-orbit and in-transit).  The calculator can't work effectively "
                                          + "on ships like that.  Build the sub-ships individually, check the calculator and warnings panel "
                                          + "on each one, then merge them together (using either sub-assemblies or the 'Merge' button on the "
                                          + "load screen."));
            }

            string requiredSituationString;

            if (!producers.Any() || producers[0].Output.ProductionRestriction == ProductionRestriction.Space)
            {
                requiredSituationString = "in space";
            }
            else
            {
                string body = producers.Select(p => p.Body).FirstOrDefault(b => !string.IsNullOrEmpty(b));
                requiredSituationString = producers[0].Output.ProductionRestriction == ProductionRestriction.LandedOnBody
                    ? $"landed at {body}" : $"in orbit of {body}";
            }

            // Calculator
            //   Duration: [_____] Days  [x] Landed
            //   Consumes:
            //    ...
            //    [[Fill Cans+10%]] [[Fill Cans+25%]]
            //   Produces:

            RecalculateResults();

            return(new DialogGUIVerticalLayout(
                       new DialogGUISpace(3),
                       new DialogGUIHorizontalLayout(TextAnchor.MiddleLeft,
                                                     new DialogGUILabel("Duration:"),
                                                     new DialogGUITextInput(
                                                         txt: this.plannedMissionDuration.ToString(),
                                                         multiline: false,
                                                         maxlength: 5,
                                                         textSetFunc: OnInputText,
                                                         getString: () => this.plannedMissionDuration.ToString(),
                                                         contentType: TMPro.TMP_InputField.ContentType.IntegerNumber,
                                                         hght: 24f),
                                                     new DialogGUILabel("days while " + requiredSituationString),
                                                     new DialogGUIFlexibleSpace()
                                                     ),
                       new DialogGUISpace(3),
                       new DialogGUILabel("<b>Production:</b>"),
                       new DialogGUILabel(() => this.productionInfo),
                       new DialogGUISpace(3),
                       new DialogGUILabel("<b>Consumption:</b>"),
                       new DialogGUILabel(() => this.consumptionInfo),
                       new DialogGUISpace(3),
                       new DialogGUILabel(() => string.IsNullOrEmpty(this.productionLimitedBy) ? "" : TextEffects.Yellow("<b>Production Limited By:</b>")),
                       new DialogGUILabel(() => this.productionLimitedBy)));
        }
Beispiel #8
0
 internal static string StarvingKerbals(List <CrewDescriptor> crewInBucket)
 {
     return(TextEffects.Red($"{GetGroupDescription(crewInBucket)} {isare(crewInBucket)} refusing to do any more work and {isare(crewInBucket)} contemplating legal action against KSP!  Get {himherthem(crewInBucket)} home or get some food out here right away!"));
 }