Esempio n. 1
0
        public ResourceLode GetOrCreateResourceLoad(Vessel scannerVessel, Vessel nearVessel, TechTier tier, double scannerNetQuality)
        {
            // There's only allowed one resource load - you have to harvest it until it's gone
            // So find the thing first.
            var lode = this.activeLodes.FirstOrDefault(rl => rl.bodyName == nearVessel.mainBody.name && rl.Tier == tier);

            if (lode != null)
            {
                // Ensure that there's a waypoint
                if (Waypoints.TryFindWaypointById(lode.Identifier, out _))
                {
                    ScreenMessages.PostScreenMessage("A lode has already been identified - look for a waypoint on the surface");
                }
                else
                {
                    Waypoints.CreateWaypointAt($"Loose Crushins ({tier.DisplayName()})", nearVessel.mainBody, lode.Latitude, lode.Longitude);
                    ScreenMessages.PostScreenMessage("A lode has already been identified - the waypoint was recreated");
                }
            }
            else
            {
                var waypoint = Waypoints.CreateWaypointNear(
                    $"Loose Crushins ({tier.DisplayName()})", nearVessel, 10000, 500000,
                    scannerNetQuality, nearVessel.situation == Vessel.Situations.SPLASHED);
                lode = new ResourceLode(waypoint, tier);
                activeLodes.Add(lode);

                PopupMessageWithKerbal.ShowPopup(
                    "Lookie What I Found!",
                    CrewBlurbs.CreateMessage(
                        scannerNetQuality < PksScanner.BadScannerNetQualityThreshold ? "#LOC_KPBS_SCANNER_FIND_NOSATS" : "#LOC_KPBS_SCANNER_FIND_SATS",
                        scannerVessel.GetVesselCrew(),
                        new string[] { nameof(PksScanningSkill) }, tier),
                    "A waypoint has been created - you need to land a ship or drive a rover with a portable digger "
                    + "to within 150m of the waypoint, deploy the drill, fill your tanks with CrushIns, haul the "
                    + "load back to the base and unload it using the resource-transfer mechanism on the colony "
                    + "status screen (the cupcake button).  After you've dumped two loads with the same craft, "
                    + "the crew at the base will be able to automatically gather resources in the future."
                    + "\r\n\r\n"
                    + "The more scanner satellites you have in polar orbit, the more likely you are to get a location "
                    + "near your base.",
                    "On it");
            }

            return(lode);
        }
Esempio n. 2
0
        internal static IEnumerable <WarningMessage> CheckTieredProduction(IColonizationResearchScenario colonizationResearch, List <ITieredProducer> producers, Dictionary <string, double> amountAvailable, Dictionary <string, double> storageAvailable)
        {
            List <ITieredProducer> noScannerParts     = new List <ITieredProducer>();
            List <ITieredProducer> noSubResearchParts = new List <ITieredProducer>();
            List <ITieredProducer> underTier          = new List <ITieredProducer>();

            foreach (var producer in producers)
            {
                var suitability = StaticAnalysis.GetTierSuitability(colonizationResearch, producer.Output, producer.Tier, producer.MaximumTier, producer.Body);
                switch (suitability)
                {
                case TierSuitability.LacksScanner:
                    noScannerParts.Add(producer);
                    break;

                case TierSuitability.LacksSubordinateResearch:
                    noSubResearchParts.Add(producer);
                    break;

                case TierSuitability.UnderTier:
                    underTier.Add(producer);
                    break;

                default:
                    break;
                }
            }

            if (noScannerParts.Any())
            {
                var examplePart = noScannerParts[0];
                yield return(new WarningMessage
                {
                    Message = $"Scanning technology at {examplePart.Body} has not kept up with production technologies - {examplePart.Tier.DisplayName()} parts will not function until you deploy an equal-tier scanner to orbit around {examplePart.Body}.",
                    IsClearlyBroken = true,
                    FixIt = () => SetToIdealTier(colonizationResearch, noScannerParts)
                });
            }

            if (noSubResearchParts.Any())
            {
                var examplePart = noSubResearchParts[0];
                yield return(new WarningMessage
                {
                    Message = $"Not all the products in the production chain for {examplePart.Output.DisplayName} have advanced to {examplePart.Tier}.",
                    IsClearlyBroken = true,
                    FixIt = () => SetToIdealTier(colonizationResearch, noSubResearchParts)
                });
            }

            if (underTier.Any())
            {
                var examplePart = underTier[0];
                yield return(new WarningMessage
                {
                    Message = $"This base is not taking advantage of the latest tech for producing {examplePart.Output.DisplayName}",
                    IsClearlyBroken = true,
                    FixIt = () => SetToIdealTier(colonizationResearch, underTier)
                });
            }

            var mostUsedBodyAndCount = producers
                                       .Where(c => c.Output.ProductionRestriction != ProductionRestriction.Space)
                                       .Where(c => c.Body != null)
                                       .GroupBy(c => c.Body)
                                       .Select(g => new { body = g.Key, count = g.Count() })
                                       .OrderByDescending(o => o.count)
                                       .ToArray();
            string targetBody = mostUsedBodyAndCount.Length > 0 ? mostUsedBodyAndCount[0].body : null;

            foreach (var pair in producers.GroupBy(producer => producer.Output))
            {
                TieredResource output = pair.Key;
                IEnumerable <ITieredProducer> parts = pair;

                // Parts should be set consistently
                TechTier minTier = parts.Min(p => p.Tier);
                TechTier maxTier = parts.Max(p => p.Tier);
                if (minTier != maxTier)
                {
                    yield return(new WarningMessage
                    {
                        Message = $"Not all of the parts producing {output.BaseName} are set at {maxTier}",
                        IsClearlyBroken = false,
                        FixIt = () =>
                        {
                            foreach (var part in parts)
                            {
                                part.Tier = maxTier;
                            }
                        }
                    });

                    break;
                }

                // Supplier parts should be at least maxTier
                var            firstPart = parts.First();
                TieredResource input     = firstPart.Input;
                if (parts.First().Input == null && output.IsHarvestedLocally && targetBody != null)
                {
                    // then it depends on scanning
                    TechTier maxScanningTier = colonizationResearch.GetMaxUnlockedScanningTier(targetBody);
                    if (maxTier > maxScanningTier)
                    {
                    }
                }
                else if (input != null)
                {
                    // Ensure that the suppliers are all at least the same tier.
                    if (producers.Any(producer => producer.Output == input && producer.Tier < maxTier))
                    {
                        yield return(new WarningMessage
                        {
                            Message = $"There are {maxTier.DisplayName()} producers of {output.BaseName}, but it requires equal-tier {input.BaseName} production in order to work.",
                            IsClearlyBroken = true,
                            FixIt = null
                        });
                    }
                }
            }
        }
Esempio n. 3
0
        public static string CreateMessage(string baseMessageTag, List <ProtoCrewMember> crew, IEnumerable <string> experienceEffects, TechTier tier)
        {
            HashSet <string> possibleTraits = new HashSet <string>(
                experienceEffects.SelectMany(effect => GameDatabase.Instance
                                             .ExperienceConfigs
                                             .GetTraitsWithEffect(effect)));
            var            crewDescriptors = crew.Select(c => FromKsp(c, protocrew => possibleTraits.Contains(protocrew.trait))).ToList();
            CrewDescriptor perp            = null;
            CrewDescriptor victim          = null;

            string Replacer(Match match)
            {
                switch (match.Value)
                {
                case "[tier]":
                    return(tier.DisplayName());

                case "[perp_name]":
                    perp = perp ?? ChoosePerpetrator(crewDescriptors);
                    return(perp.Name);

                case "[victim_name]":
                    victim = victim ?? ChooseVictim(crewDescriptors);
                    return(victim.Name);

                case "[perp_heshe]":
                    perp = perp ?? ChoosePerpetrator(crewDescriptors);
                    return(perp.heshe);

                case "[perp_himher]":
                    perp = perp ?? ChoosePerpetrator(crewDescriptors);
                    return(perp.himher);

                case "[perp_hisher]":
                    perp = perp ?? ChoosePerpetrator(crewDescriptors);
                    return(perp.hisher);

                case "[victim_hisher]":
                    victim = victim ?? ChooseVictim(crewDescriptors);
                    return(victim.hisher);

                case "[perps]":
                    return(GetGroupDescription(crewDescriptors, true));

                case "[victims]":
                    return(GetGroupDescription(crewDescriptors, false));

                case "[victims_hashave]":
                    return(crewDescriptors.hashave());

                case "[victims_isare]":
                    return(crewDescriptors.isare());

                case "[victims_hishertheir]":
                    return(crewDescriptors.hishertheir());

                case "[crew]":
                    return(GetGroupDescription(crewDescriptors));

                case "[resource_name]":
                    return(GetMessage("LOC_KPBS_RANDOM_MINERAL"));

                case "[body]":
                    return(FlightGlobals.ActiveVessel.mainBody.name);

                default:
                    return($"?Unknown Substitution: {match.Value}?");
                }
            }

            string message = replacement.Replace(GetMessage(baseMessageTag), Replacer);

            return(message);
        }
        /// <summary>
        ///   Calculates snacks consumption aboard the vessel.
        /// </summary>
        /// <param name="crew">The crew</param>
        /// <param name="deltaTime">The amount of time (in seconds) since the last calculation was done</param>
        /// <returns>The amount of <paramref name="deltaTime"/> in which food was supplied.</returns>
        private void ProduceAndConsume(List <ProtoCrewMember> crew, double deltaTime)
        {
            var tieredProducers = this.vessel.FindPartModulesImplementing <ITieredProducer>();
            var combiners       = this.vessel.FindPartModulesImplementing <ITieredCombiner>();

            this.ResourceQuantities(out var availableResources, out var availableStorage);

            // Create a mock producer for the CrushIns which we're auto-supplying.  Also remove it
            // from the list of available storage, else the algorithm will attempt to fill it.
            foreach (var resourceName in availableResources.Keys.Where(resourceName => this.ResourceIsAutosupplied(resourceName)))
            {
                tieredProducers.Add(new AutoMinerProducer(resourceName));
                availableStorage.Remove(resourceName);
            }

            var    crewPart      = vessel.parts.FirstOrDefault(p => p.CrewCapacity > 0);
            double remainingTime = deltaTime;

            while (remainingTime > ResourceUtilities.FLOAT_TOLERANCE)
            {
                TieredProduction.CalculateResourceUtilization(
                    crew.Count,
                    remainingTime,
                    tieredProducers,
                    combiners,
                    ColonizationResearchScenario.Instance,
                    availableResources,
                    availableStorage,
                    out double elapsedTime,
                    out List <TieredResource> breakthroughCategories,
                    out Dictionary <string, double> resourceConsumptionPerSecond,
                    out Dictionary <string, double> resourceProductionPerSecond,
                    out var _, out var _);

                if (elapsedTime == 0)
                {
                    LifeSupportScenario.Instance.KerbalsMissedAMeal(this.vessel,
                                                                    hasActiveProducers: tieredProducers.Any(p => p.IsProductionEnabled));
                    break;
                }

                if (resourceConsumptionPerSecond != null || resourceProductionPerSecond != null)
                {
                    ConversionRecipe consumptionRecipe = new ConversionRecipe();
                    if (resourceConsumptionPerSecond != null)
                    {
                        foreach (var pair in resourceConsumptionPerSecond)
                        {
                            ColonizationResearchScenario.Instance.TryParseTieredResourceName(pair.Key, out var consumedResource, out var consumedResourceTier);
                            if (consumedResource == ColonizationResearchScenario.LodeResource &&
                                ResourceLodeScenario.Instance.TryFindResourceLodeInRange(vessel, consumedResourceTier, out var resourceLode))
                            {
                                ResourceLodeScenario.Instance.TryConsume(resourceLode, pair.Value * elapsedTime, out _);
                            }
                            else
                            {
                                double newAmount = availableResources[pair.Key] - pair.Value * elapsedTime;
                                if (newAmount < ResourceUtilities.FLOAT_TOLERANCE)
                                {
                                    availableResources.Remove(pair.Key);
                                }
                                else
                                {
                                    availableResources[pair.Key] = newAmount;
                                }

                                consumptionRecipe.Inputs.Add(new ResourceRatio()
                                {
                                    ResourceName = pair.Key,
                                    Ratio        = pair.Value,
                                    DumpExcess   = false,
                                    FlowMode     = ResourceFlowMode.ALL_VESSEL
                                });
                            }
                        }
                    }
                    if (resourceProductionPerSecond != null)
                    {
                        foreach (var pair in resourceProductionPerSecond)
                        {
                            double newAmount = availableStorage[pair.Key] - elapsedTime * pair.Value;
                            if (newAmount < ResourceUtilities.FLOAT_TOLERANCE)
                            {
                                availableStorage.Remove(pair.Key);
                            }
                            else
                            {
                                availableStorage[pair.Key] = newAmount;
                            }
                        }

                        consumptionRecipe.Outputs.AddRange(
                            resourceProductionPerSecond.Select(pair => new ResourceRatio()
                        {
                            ResourceName = pair.Key,
                            Ratio        = pair.Value,
                            DumpExcess   = true,
                            FlowMode     = ResourceFlowMode.ALL_VESSEL
                        }));
                    }

                    var consumptionResult = this.ResConverter.ProcessRecipe(elapsedTime, consumptionRecipe, crewPart, null, 1f);
                    Debug.Assert(Math.Abs(consumptionResult.TimeFactor - elapsedTime) < ResourceUtilities.FLOAT_TOLERANCE,
                                 "ProgressiveColonizationSystem.SnackConsumption.CalculateSnackFlow is busted - it somehow got the consumption recipe wrong.");
                }

                foreach (TieredResource resource in breakthroughCategories)
                {
                    TechTier newTier       = ColonizationResearchScenario.Instance.GetMaxUnlockedTier(resource, this.vessel.lastBody.name);
                    string   title         = $"{resource.ResearchCategory.DisplayName} has progressed to {newTier.DisplayName()}!";
                    string   message       = resource.ResearchCategory.BreakthroughMessage(crew, newTier);
                    string   boringMessage = resource.ResearchCategory.BoringBreakthroughMessage(crew, newTier);
                    PopupMessageWithKerbal.ShowPopup(title, message, boringMessage, "That's Just Swell");
                }

                remainingTime -= elapsedTime;

                double lastMealTime = Planetarium.GetUniversalTime() - remainingTime;
                LifeSupportScenario.Instance.KerbalsHadASnack(this.vessel, lastMealTime);
            }
        }
        private void CalculateTierLabels(bool setTier)
        {
            for (TechTier tier = TechTier.Tier0; tier <= TechTier.Tier4; ++tier)
            {
                var  suitability = StaticAnalysis.GetTierSuitability(ColonizationResearchScenario.Instance, this.Product, tier, this.MaxTierForPart, this.Body);
                bool isEnabled;
                Func <string, string> transform;
                string toolTipTag;
                switch (suitability)
                {
                default:
                case TierSuitability.Ideal:
                    transform  = TextEffects.Green;
                    toolTipTag = "#LOC_KPBS_IDEAL_TIER";
                    isEnabled  = true;
                    if (setTier)
                    {
                        this.RiskLevel = TierSuitability.Ideal;
                        this.Tier      = tier;
                    }
                    break;

                case TierSuitability.LacksScanner:
                    transform  = TextEffects.Yellow;
                    toolTipTag = "#LOC_KPBS_SCANNING_SKILL_LAGS";
                    isEnabled  = true;
                    break;

                case TierSuitability.LacksSubordinateResearch:
                    transform  = TextEffects.Red;
                    toolTipTag = "#LOC_KPBS_SUBORDINATE_SKILL_LAGS";
                    isEnabled  = true;
                    break;

                case TierSuitability.UnderTier:
                    transform  = TextEffects.Yellow;
                    toolTipTag = "#LOC_KPBS_UNDER_TIER";
                    isEnabled  = true;
                    break;

                case TierSuitability.NotResearched:
                    transform  = s => s;
                    toolTipTag = "#LOC_KPBS_NOT_RESEARCHED";
                    isEnabled  = false;
                    break;

                case TierSuitability.BodyNotSelected:
                    transform  = s => s;
                    toolTipTag = "#LOC_KPBS_CHOOSE_A_BODY_FIRST";
                    isEnabled  = false;
                    break;

                case TierSuitability.PartDoesntSupportTier:
                    transform  = s => s;
                    toolTipTag = "#LOC_KPBS_PART_DOES_NOT_SUPPORT_TIER";
                    isEnabled  = false;
                    break;
                }

                TechTier techTierCopy = tier;
                string labelGetter() => transform(techTierCopy.DisplayName());

                void onToggled(bool isSelected)
                {
                    if (isSelected && isEnabled)
                    {
                        this.Tier      = techTierCopy;
                        this.RiskLevel = suitability;
                    }
                }

                if (this.tierToggles[(int)tier] == null)
                {
                    this.tierToggles[(int)tier] = new DialogGUIToggle(() => techTierCopy == this.Tier, labelGetter, onToggled);
                }
                else
                {
                    this.tierToggles[(int)tier].setLabel  = labelGetter;
                    this.tierToggles[(int)tier].onToggled = onToggled;
                }

                this.tierToggles[(int)tier].OptionInteractableCondition = () => isEnabled;
                this.tierToggles[(int)tier].tooltipText = Localizer.GetStringByTag(toolTipTag);
            }
        }