private static IEnumerable <ScienceSubject> GetSubjects(ScienceExperiment experiment, CelestialBody body, Func <string, bool> biomeFilter, bool difficult) { IEnumerable <ExperimentSituations> situations = Enum.GetValues(typeof(ExperimentSituations)).Cast <ExperimentSituations>(); // Set up the biome filter bool biomesFiltered = biomeFilter != null; if (biomeFilter == null) { biomeFilter = new Func <string, bool>(x => true); } IEnumerable <string> biomes = body.BiomeMap == null?Enumerable.Empty <string>() : body.BiomeMap.Attributes.Select(attr => attr.name.Replace(" ", string.Empty)). Where(biomeFilter); return(situations .Where(sit => ExperimentAvailable(experiment, sit, body) && (sit != ExperimentSituations.SrfSplashed || body.ocean) && ((sit != ExperimentSituations.FlyingLow && sit != ExperimentSituations.FlyingHigh) || body.atmosphere)) .SelectMany <ExperimentSituations, ScienceSubject>(sit => { if (experiment.BiomeIsRelevantWhile(sit)) { ExperimentRules rules = GetExperimentRules(experiment.id); return biomes.Where(biome => !(BiomeTracker.IsDifficult(body, biome, sit) || experiment.id == "asteroidSample") ^ difficult) .Select(biome => ScienceSubject(experiment, sit, body, biome)) .Union(body.isHomeWorld && !rules.disallowKSC && sit == ExperimentSituations.SrfLanded // static KSC items can only be landed ? Biome.KSCBiomes.Where(biomeFilter).Where(b => experiment.id == "asteroidSample" ^ !difficult).Select( staticName => ScienceSubject(experiment, ExperimentSituations.SrfLanded, body, staticName)) : Enumerable.Empty <ScienceSubject>()); } else if (experiment.id.StartsWith("ROCScience") && biomesFiltered) { ROCDefinition roc = ROCManager.Instance.rocDefinitions.Where(r => r.myCelestialBodies.Any(x => x.name == body.name) && experiment.id.Contains(r.type)).FirstOrDefault(); if (roc != null && roc.myCelestialBodies.First().biomes.Where(biomeFilter).Any()) { return new ScienceSubject[] { ScienceSubject(experiment, sit, body, "") }; } else { return Enumerable.Empty <ScienceSubject>(); } } else if (!biomesFiltered && !difficult) { return new ScienceSubject[] { ScienceSubject(experiment, sit, body, "") }; } else { return Enumerable.Empty <ScienceSubject>(); } })); }
/// <summary> /// parts that have experiments can't get their module info (what is shown in the VAB tooltip) correctly setup /// because the ExperimentInfo database isn't available at loading time, so we recompile their info manually. /// </summary> public void CompileModuleInfos() { if (PartLoader.LoadedPartsList == null) { Lib.Log("Dazed and confused: PartLoader.LoadedPartsList == null"); return; } foreach (AvailablePart ap in PartLoader.LoadedPartsList) { if (ap == null || ap.partPrefab == null) { Lib.Log("AvailablePart is null or without prefab: " + ap); continue; } foreach (PartModule module in ap.partPrefab.Modules) { if (module is Experiment expModule) { // don't show configurable experiments if (!expModule.isConfigurable && expModule.experiment_id == ExperimentId) { expModule.ExpInfo = this; // get module info for the ExperimentInfo, once if (string.IsNullOrEmpty(ModuleInfo)) { ModuleInfo = Lib.Color(Title, Lib.Kolor.Cyan, true); ModuleInfo += "\n"; ModuleInfo += expModule.GetInfo(); } } } if (!string.IsNullOrEmpty(ModuleInfo)) { continue; } if (module is ModuleScienceExperiment stockExpModule) { if (stockExpModule.experimentID == ExperimentId) { ModuleInfo = Lib.Color(Title, Lib.Kolor.Cyan, true); ModuleInfo += "\n" + Local.Experimentinfo_Datasize + ": "; //Data size ModuleInfo += Lib.HumanReadableDataSize(DataSize); if (stockExpModule.xmitDataScalar < Science.maxXmitDataScalarForSample) { ModuleInfo += "\n" + Local.Experimentinfo_generatesample; //Will generate a sample. ModuleInfo += "\n" + Local.Experimentinfo_Samplesize + " "; //Sample size: ModuleInfo += Lib.HumanReadableSampleSize(DataSize); } ModuleInfo += "\n\n"; ModuleInfo += Lib.Color(Local.Experimentinfo_Situations, Lib.Kolor.Cyan, true); //"Situations:\n" foreach (string s in AvailableSituations()) { ModuleInfo += Lib.BuildString("• <b>", s, "</b>\n"); } ModuleInfo += "\n"; ModuleInfo += stockExpModule.GetInfo(); } } else if (module is ModuleGroundExperiment groundExpModule) { if (groundExpModule.experimentId == ExperimentId) { ModuleInfo = Lib.Color(Title, Lib.Kolor.Cyan, true); ModuleInfo += "\n" + Local.Experimentinfo_Datasize + ": "; //Data size ModuleInfo += Lib.HumanReadableDataSize(DataSize); ModuleInfo += "\n\n"; ModuleInfo += groundExpModule.GetInfo(); } } } // special cases if (ExperimentId == "asteroidSample" || ExperimentId.StartsWith("cometSample_", StringComparison.Ordinal)) { ModuleInfo = Local.Experimentinfo_Asteroid; //"Asteroid samples can be taken by kerbals on EVA" ModuleInfo += "\n" + Local.Experimentinfo_Samplesize + " "; //Sample size: ModuleInfo += Lib.HumanReadableSampleSize(DataSize); ModuleInfo += "\n" + Local.Experimentinfo_Samplemass + " "; //Sample mass: ModuleInfo += Lib.HumanReadableMass(DataSize * Settings.AsteroidSampleMassPerMB); } else if (IsROC) { string rocType = ExperimentId.Substring(ExperimentId.IndexOf('_') + 1); ROCDefinition rocDef = ROCManager.Instance.rocDefinitions.Find(p => p.type == rocType); if (rocDef != null) { ModuleInfo = Lib.Color(rocDef.displayName, Lib.Kolor.Cyan, true); ModuleInfo += "\n- " + Local.Experimentinfo_scannerarm; //Analyse with a scanner arm ModuleInfo += "\n " + Local.Experimentinfo_Datasize + ": "; //Data size ModuleInfo += Lib.HumanReadableDataSize(DataSize); if (rocDef.smallRoc) { ModuleInfo += "\n- " + Local.Experimentinfo_smallRoc; //Collectable on EVA as a sample" ModuleInfo += "\n" + Local.Experimentinfo_Samplesize + " "; //Sample size: ModuleInfo += Lib.HumanReadableSampleSize(DataSize); } else { ModuleInfo += "\n- " + Local.Experimentinfo_smallRoc2; //Can't be collected on EVA } foreach (RocCBDefinition body in rocDef.myCelestialBodies) { ModuleInfo += Lib.Color("\n\n" + Local.Experimentinfo_smallRoc3.Format(body.name), Lib.Kolor.Cyan, true); //"Found on <<1>>'s :" foreach (string biome in body.biomes) { ModuleInfo += "\n- "; ModuleInfo += biome; } } } } } }
/// <summary> /// parts that have experiments can't get their module info (what is shown in the VAB tooltip) correctly setup /// because the ExperimentInfo database isn't available at loading time, so we recompile their info manually. /// </summary> public void SetupPrefabs() { if (PartLoader.LoadedPartsList == null) { Lib.Log("Dazed and confused: PartLoader.LoadedPartsList == null"); return; } foreach (AvailablePart ap in PartLoader.LoadedPartsList) { if (ap == null || ap.partPrefab == null) { Lib.Log("AvailablePart is null or without prefab: " + ap); continue; } bool partHasExperimentModule = false; foreach (PartModule module in ap.partPrefab.Modules) { if (module is Experiment expModule) { if (expModule.experiment_id == ExperimentId) { expModule.ExpInfo = this; // works inside the ExperimentInfo ctor, but make sure it's called at the end of it. partHasExperimentModule = true; // get module info for the ExperimentInfo, once if (string.IsNullOrEmpty(ModuleInfo)) { ModuleInfo = Lib.Color(Title, Lib.Kolor.Cyan, true); ModuleInfo += "\n"; ModuleInfo += expModule.GetInfo(); } } } if (!string.IsNullOrEmpty(ModuleInfo)) { continue; } if (module is ModuleScienceExperiment stockExpModule) { if (stockExpModule.experimentID == ExperimentId) { ModuleInfo = Lib.Color(Title, Lib.Kolor.Cyan, true); ModuleInfo += "\nData size: "; ModuleInfo += Lib.HumanReadableDataSize(DataSize); if (stockExpModule.xmitDataScalar < Science.maxXmitDataScalarForSample) { ModuleInfo += "\nWill generate a sample."; ModuleInfo += "\nSample size: "; ModuleInfo += Lib.HumanReadableSampleSize(DataSize); } ModuleInfo += "\n\n"; ModuleInfo += Lib.Color("Situations:\n", Lib.Kolor.Cyan, true); foreach (string s in AvailableSituations()) { ModuleInfo += Lib.BuildString("• <b>", s, "</b>\n"); } ModuleInfo += "\n"; ModuleInfo += stockExpModule.GetInfo(); } } #if !KSP15_16 else if (module is ModuleGroundExperiment groundExpModule) { if (groundExpModule.experimentId == ExperimentId) { ModuleInfo = Lib.Color(Title, Lib.Kolor.Cyan, true); ModuleInfo += "\nData size: "; ModuleInfo += Lib.HumanReadableDataSize(DataSize); ModuleInfo += "\n\n"; ModuleInfo += groundExpModule.GetInfo(); } } #endif } // special cases if (ExperimentId == "asteroidSample") { ModuleInfo = "Asteroid samples can be taken by kerbals on EVA"; ModuleInfo += "\nSample size: "; ModuleInfo += Lib.HumanReadableSampleSize(DataSize); ModuleInfo += "\nSample mass: "; ModuleInfo += Lib.HumanReadableMass(DataSize * Settings.AsteroidSampleMassPerMB); } #if !KSP15_16 else if (IsROC) { string rocType = ExperimentId.Substring(ExperimentId.IndexOf('_') + 1); ROCDefinition rocDef = ROCManager.Instance.rocDefinitions.Find(p => p.type == rocType); if (rocDef != null) { ModuleInfo = Lib.Color(rocDef.displayName, Lib.Kolor.Cyan, true); ModuleInfo += "\n- Analyse with a scanner arm"; ModuleInfo += "\n Data size: "; ModuleInfo += Lib.HumanReadableDataSize(DataSize); if (rocDef.smallRoc) { ModuleInfo += "\n- Collectable on EVA as a sample"; ModuleInfo += "\nSample size: "; ModuleInfo += Lib.HumanReadableSampleSize(DataSize); } else { ModuleInfo += "\n- Can't be collected on EVA"; } foreach (RocCBDefinition body in rocDef.myCelestialBodies) { ModuleInfo += Lib.Color("\n\nFound on " + body.name + "'s :", Lib.Kolor.Cyan, true); foreach (string biome in body.biomes) { ModuleInfo += "\n- "; ModuleInfo += biome; } } } } #endif if (partHasExperimentModule && !ap.name.StartsWith("kerbalEVA")) { ap.moduleInfos.Clear(); ap.resourceInfos.Clear(); try { Lib.ReflectionCall(PartLoader.Instance, "CompilePartInfo", new Type[] { typeof(AvailablePart), typeof(Part) }, new object[] { ap, ap.partPrefab }); } catch (Exception ex) { Lib.Log("Could not patch the moduleInfo for part " + ap.name + " - " + ex.Message + "\n" + ex.StackTrace); } } } }