/// <summary> get the stock ExperimentSituation for our ScienceSituation value </summary> // Note: any modification in this should be reflected in ValidateSituationBitMask() public static ScienceSituation ToValidStockSituation(this ScienceSituation situation) { switch (situation) { case ScienceSituation.SrfLanded: return(ScienceSituation.SrfLanded); case ScienceSituation.SrfSplashed: return(ScienceSituation.SrfSplashed); case ScienceSituation.FlyingLow: return(ScienceSituation.FlyingLow); case ScienceSituation.FlyingHigh: return(ScienceSituation.FlyingHigh); case ScienceSituation.InSpaceLow: return(ScienceSituation.InSpaceLow); case ScienceSituation.InSpaceHigh: return(ScienceSituation.InSpaceHigh); case ScienceSituation.Surface: return(ScienceSituation.SrfLanded); case ScienceSituation.Flying: return(ScienceSituation.FlyingHigh); case ScienceSituation.Space: case ScienceSituation.BodyGlobal: default: return(ScienceSituation.InSpaceLow); } }
public void RemoveSubject(ExperimentInfo expInfo, int bodyIndex, ScienceSituation scienceSituation, int biomeIndex, SubjectData subjectData) { BodiesSituationsBiomesSubject bodiesSituationsBiomesSubject; if (!TryGetValue(expInfo, out bodiesSituationsBiomesSubject)) { return; } SituationsBiomesSubject situationsBiomesSubject; if (!bodiesSituationsBiomesSubject.TryGetValue(bodyIndex, out situationsBiomesSubject)) { return; } BiomesSubject biomesSubject; if (!situationsBiomesSubject.TryGetValue(scienceSituation, out biomesSubject)) { return; } List <SubjectData> subjectDataList; if (!biomesSubject.TryGetValue(biomeIndex, out subjectDataList)) { return; } subjectDataList.Remove(subjectData); }
public static bool IsAvailableOnBody(this ScienceSituation situation, CelestialBody body) { switch (situation) { case ScienceSituation.SrfLanded: case ScienceSituation.Surface: if (!body.hasSolidSurface) { return(false); } break; case ScienceSituation.SrfSplashed: if (!body.ocean || !body.hasSolidSurface) { return(false); } break; case ScienceSituation.FlyingLow: case ScienceSituation.FlyingHigh: case ScienceSituation.Flying: if (!body.atmosphere) { return(false); } break; case ScienceSituation.None: return(false); } return(true); }
public static string Serialize(this ScienceSituation situation) { switch (situation) { case ScienceSituation.SrfLanded: return("SrfLanded"); case ScienceSituation.SrfSplashed: return("SrfSplashed"); case ScienceSituation.FlyingLow: return("FlyingLow"); case ScienceSituation.FlyingHigh: return("FlyingHigh"); case ScienceSituation.InSpaceLow: return("InSpaceLow"); case ScienceSituation.InSpaceHigh: return("InSpaceHigh"); case ScienceSituation.Surface: return("Surface"); case ScienceSituation.Flying: return("Flying"); case ScienceSituation.Space: return("Space"); case ScienceSituation.BodyGlobal: return("BodyGlobal"); default: return("None"); } }
public void AddSubject(int bodyIndex, ScienceSituation scienceSituation, int biomeIndex, SubjectData subjectData) { SituationsBiomesSubjects situationsBiomesSubjects; if (!TryGetValue(bodyIndex, out situationsBiomesSubjects)) { situationsBiomesSubjects = new SituationsBiomesSubjects(); Add(bodyIndex, situationsBiomesSubjects); } BiomesSubjects biomesSubjects; if (!situationsBiomesSubjects.TryGetValue(scienceSituation, out biomesSubjects)) { biomesSubjects = new BiomesSubjects(); situationsBiomesSubjects.Add(scienceSituation, biomesSubjects); } List <SubjectData> subjects; if (!biomesSubjects.TryGetValue(biomeIndex, out subjects)) { subjects = new List <SubjectData>(); biomesSubjects.Add(biomeIndex, subjects); } if (subjectData != null) { subjects.Add(subjectData); } }
public static uint BitValue(this ScienceSituation situation) { switch (situation) { case ScienceSituation.None: return(0); case ScienceSituation.SrfLanded: return(1); // 1 << 0 case ScienceSituation.SrfSplashed: return(2); // 1 << 1 case ScienceSituation.FlyingLow: return(4); // 1 << 2 case ScienceSituation.FlyingHigh: return(8); // 1 << 3 case ScienceSituation.InSpaceLow: return(16); // 1 << 4 case ScienceSituation.InSpaceHigh: return(32); // 1 << 5 case ScienceSituation.Surface: return(2048); // 1 << 11 case ScienceSituation.Flying: return(4096); // 1 << 12 case ScienceSituation.Space: return(8192); // 1 << 13 case ScienceSituation.BodyGlobal: return(16384); // 1 << 14 default: return(0); } }
public static string Title(this ScienceSituation situation) { switch (situation) { case ScienceSituation.None: return(Local.Situation_None); //"none" case ScienceSituation.SrfLanded: return(Local.Situation_Landed); //"landed" case ScienceSituation.SrfSplashed: return(Local.Situation_Splashed); //"splashed" case ScienceSituation.FlyingLow: return(Local.Situation_Flyinglow); //"flying low" case ScienceSituation.FlyingHigh: return(Local.Situation_Flyinghigh); //"flying high" case ScienceSituation.InSpaceLow: return(Local.Situation_Spacelow); //"space low" case ScienceSituation.InSpaceHigh: return(Local.Situation_SpaceHigh); //"space high" case ScienceSituation.Surface: return(Local.Situation_Surface); //"surface" case ScienceSituation.Flying: return(Local.Situation_Flying); //"flying" case ScienceSituation.Space: return(Local.Situation_Space); //"space" case ScienceSituation.BodyGlobal: return(Local.Situation_BodyGlobal); //"global" default: return(Local.Situation_None); //"none" } }
public void RemoveSubject(int bodyIndex, ScienceSituation scienceSituation, int biomeIndex, SubjectData subjectData) { SituationsBiomesSubjects situationsBiomesSubjects; if (!TryGetValue(bodyIndex, out situationsBiomesSubjects)) { return; } BiomesSubjects biomesSubjects; if (!situationsBiomesSubjects.TryGetValue(scienceSituation, out biomesSubjects)) { return; } List <SubjectData> subjects; if (!biomesSubjects.TryGetValue(biomeIndex, out subjects)) { return; } subjects.Remove(subjectData); }
public Situation GetExperimentSituation(ExperimentInfo expInfo) { ScienceSituation expSituation = ScienceSituation.None; foreach (ScienceSituation situation in situations) { if (situation.IsAvailableForExperiment(expInfo)) { expSituation = situation; break; } } int expBiomeIndex = biomeIndex; if (expSituation.IsVirtualBiomesRelevantForExperiment(expInfo)) { foreach (VirtualBiome virtualBiome in virtualBiomes) { if (expInfo.VirtualBiomes.Contains(virtualBiome)) { expBiomeIndex = (int)virtualBiome; break; } } } return(new Situation(body.flightGlobalsIndex, expSituation, expBiomeIndex)); }
public static int FieldsToId(int bodyIndex, ScienceSituation situation, int biomeIndex = -1) { if (biomeIndex < 0) { biomeIndex = agnosticBiomeIndex; } return(((byte)biomeIndex << 8 | (byte)situation) << 16 | (ushort)bodyIndex); }
public static int GetBiomeAgnosticIdForExperiment(int situationId, ExperimentInfo expInfo) { ScienceSituation sit = (ScienceSituation)(byte)(situationId >> 16); if (!sit.IsBiomesRelevantForExperiment(expInfo)) { return(situationId | (agnosticBiomeIndex << 24)); } return(situationId); }
private static void SetSituationBitInMask(ref uint mask, ScienceSituation situation, bool value) { if (value) { mask = (uint)((int)mask | (1 << (int)situation)); } else { mask = (uint)((int)mask & ~(1 << (int)situation)); } }
public string GetTitleForExperiment(ExperimentInfo expInfo) { if (ScienceSituation.IsBiomesRelevantForExperiment(expInfo)) { return(Lib.BuildString(BodyTitle, " ", ScienceSituationTitle, " ", BiomeTitle)); } else { return(Lib.BuildString(BodyTitle, " ", ScienceSituationTitle)); } }
public string GetStockIdForExperiment(ExperimentInfo expInfo) { if (ScienceSituation.IsBiomesRelevantForExperiment(expInfo)) { return(Lib.BuildString(BodyName, StockScienceSituationName, BiomeName)); } else { return(Lib.BuildString(BodyName, StockScienceSituationName)); } }
public Situation(int bodyIndex, ScienceSituation situation, int biomeIndex = -1) { ScienceSituation = situation; Body = FlightGlobals.Bodies[bodyIndex]; if (biomeIndex >= 0) { if (biomeIndex >= ScienceSituationUtils.minVirtualBiome) { VirtualBiome = (VirtualBiome)biomeIndex; } else if (Body.BiomeMap != null) { Biome = Body.BiomeMap.Attributes[biomeIndex]; } } Id = FieldsToId(bodyIndex, situation, biomeIndex); }
public void AddSubject(ExperimentInfo expInfo, int bodyIndex, ScienceSituation scienceSituation, int biomeIndex, SubjectData subjectData) { BodiesSituationsBiomesSubject bodiesSituationsBiomesSubject; if (!TryGetValue(expInfo, out bodiesSituationsBiomesSubject)) { bodiesSituationsBiomesSubject = new BodiesSituationsBiomesSubject(); Add(expInfo, bodiesSituationsBiomesSubject); } SituationsBiomesSubject situationsBiomesSubject; if (!bodiesSituationsBiomesSubject.TryGetValue(bodyIndex, out situationsBiomesSubject)) { situationsBiomesSubject = new SituationsBiomesSubject(); bodiesSituationsBiomesSubject.Add(bodyIndex, situationsBiomesSubject); } BiomesSubject biomesSubject; if (!situationsBiomesSubject.TryGetValue(scienceSituation, out biomesSubject)) { biomesSubject = new BiomesSubject(); situationsBiomesSubject.Add(scienceSituation, biomesSubject); } List <SubjectData> subjectDataList; if (!biomesSubject.TryGetValue(biomeIndex, out subjectDataList)) { subjectDataList = new List <SubjectData>(); biomesSubject.Add(biomeIndex, subjectDataList); } if (subjectData != null) { subjectDataList.Add(subjectData); } }
public static float BodyMultiplier(this ScienceSituation situation, CelestialBody body) { float result = 0f; switch (situation) { case ScienceSituation.Surface: case ScienceSituation.SrfLanded: result = body.scienceValues.LandedDataValue; break; case ScienceSituation.SrfSplashed: result = body.scienceValues.SplashedDataValue; break; case ScienceSituation.FlyingLow: result = body.scienceValues.FlyingLowDataValue; break; case ScienceSituation.Flying: case ScienceSituation.FlyingHigh: result = body.scienceValues.FlyingHighDataValue; break; case ScienceSituation.BodyGlobal: case ScienceSituation.Space: case ScienceSituation.InSpaceLow: result = body.scienceValues.InSpaceLowDataValue; break; case ScienceSituation.InSpaceHigh: result = body.scienceValues.InSpaceHighDataValue; break; } if (result == 0f) { Lib.Log("Science: invalid/unknown situation " + situation.ToString(), Lib.LogLevel.Error); return(1f); // returning 0 will result in NaN values } return(result); }
/// <summary> /// Create our SubjectData by parsing the stock "experiment@situation" subject id string. /// Used for asteroid samples, for compatibility with RnD archive data of removed mods and for converting stock ScienceData into SubjectData /// </summary> public static SubjectData GetSubjectDataFromStockId(string stockSubjectId, ScienceSubject RnDSubject = null) { SubjectData subjectData = null; if (unknownSubjectDatas.TryGetValue(stockSubjectId, out subjectData)) { return(subjectData); } string[] expAndSit = stockSubjectId.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries); if (expAndSit.Length != 2) { Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : bad format"); return(null); } // the recovery experiment subject are created in ResearchAndDevelopment.reverseEngineerRecoveredVessel, called on the vessel recovery event // it use a non-standard situation system ("body" + a situation enum "RecoverySituations"). We ignore those. if (expAndSit[0] == "recovery") { return(null); } ExperimentInfo expInfo = GetExperimentInfo(expAndSit[0]); if (expInfo == null) { Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : the experiment id '" + expAndSit[0] + "' doesn't exists"); return(null); } // for subject ids created with the ResearchAndDevelopment.GetExperimentSubject overload that take a "sourceUId" string, // the sourceUId is added to the situation after a "_" // in stock this seems to be used only for asteroids, and I don't think any mod use it. string extraSituationInfo = string.Empty; if (expAndSit[1].Contains("_")) { string[] sitAndAsteroid = expAndSit[1].Split('_'); // remove expAndSit[1] = sitAndAsteroid[0]; // asteroid are saved as "part.partInfo.name + part.flightID", and the part name always end with a randomly generated "AAA-000" string extraSituationInfo = Regex.Match(sitAndAsteroid[1], ".*?-[0-9][0-9][0-9]").Value; // if no match, just use the unmodified string if (extraSituationInfo == string.Empty) { extraSituationInfo = sitAndAsteroid[1]; } } string[] bodyAndBiome = expAndSit[1].Split(ScienceSituationUtils.validSituationsStrings, StringSplitOptions.RemoveEmptyEntries); string situation; if (bodyAndBiome.Length == 1) { situation = expAndSit[1].Substring(bodyAndBiome[0].Length); } else if (bodyAndBiome.Length == 2) { situation = expAndSit[1].Substring(bodyAndBiome[0].Length, expAndSit[1].Length - bodyAndBiome[0].Length - bodyAndBiome[1].Length); } else { Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : the situation doesn't exists"); return(null); } CelestialBody subjectBody = null; foreach (CelestialBody body in FlightGlobals.Bodies) { if (body.name == bodyAndBiome[0]) { subjectBody = body; break; } } if (subjectBody == null) { // TODO : DMOS asteroid experiments are doing : "magScan@AsteroidInSpaceLowCarbonaceous7051371", those subjects will be discarded entirely here // because the body "Asteroid" doesn't exists, consequently it's impossible to create the Situation object. // To handle that, maybe we could implement a derived class "UnknownSituation" from Situation that can handle a completely random subject format Lib.Log("Could not parse the SubjectData from subjectId '" + stockSubjectId + "' : the body '" + bodyAndBiome[0] + "' doesn't exist"); return(null); } ScienceSituation scienceSituation = ScienceSituationUtils.ScienceSituationDeserialize(situation); int biomeIndex = -1; if (bodyAndBiome.Length == 2 && ScienceSituationUtils.IsBodyBiomesRelevantForExperiment(scienceSituation, expInfo) && subjectBody.BiomeMap != null) { for (int i = 0; i < subjectBody.BiomeMap.Attributes.Length; i++) { // Note : a stock subject has its spaces in the biome name removed but prior versions of kerbalism didn't do that, // so we try to fix it, in order not to create duplicates in the RnD archives. // TODO : also, we need to remove the "reentry" subjects, as stock is failing to parse them, altough this is in a try/catch block and handled gracefully. string sanitizedBiome = bodyAndBiome[1].Replace(" ", string.Empty); if (RnDSubject != null && extraSituationInfo == string.Empty && sanitizedBiome != bodyAndBiome[1]) { string correctedSubjectId = expAndSit[0] + "@" + bodyAndBiome[0] + situation + sanitizedBiome; RnDSubject.id = correctedSubjectId; Dictionary <string, ScienceSubject> stockSubjects = Lib.ReflectionValue <Dictionary <string, ScienceSubject> >(ResearchAndDevelopment.Instance, "scienceSubjects"); if (stockSubjects.Remove(stockSubjectId) && !stockSubjects.ContainsKey(correctedSubjectId)) { stockSubjects.Add(correctedSubjectId, RnDSubject); } Lib.Log("RnD subject load : misformatted subject '" + stockSubjectId + "' was corrected to '" + correctedSubjectId + "'"); } if (subjectBody.BiomeMap.Attributes[i].name.Replace(" ", string.Empty).Equals(sanitizedBiome, StringComparison.OrdinalIgnoreCase)) { biomeIndex = i; break; } } } int bodyIndex = subjectBody.flightGlobalsIndex; Situation vesselSituation = new Situation(bodyIndex, scienceSituation, biomeIndex); // if the subject is a "doable" subject, we should have it in the DB. if (extraSituationInfo == string.Empty) { subjectData = GetSubjectData(expInfo, vesselSituation); } // else create the subjectdata. this can happen either because : // - it's a subject using the stock "extra id" system (asteroid samples) // - the subject was created in RnD prior to an experiment definition config change // - it was created by a mod that does things in a non-stock way (ex : DMOS anomaly scans uses the anomaly name as biomes) if (subjectData == null) { if (bodyAndBiome.Length == 2 && bodyAndBiome[1] != string.Empty && string.IsNullOrEmpty(extraSituationInfo)) { extraSituationInfo = bodyAndBiome[1]; } UnknownSubjectData unknownSubjectData = new UnknownSubjectData(expInfo, vesselSituation, stockSubjectId, RnDSubject, extraSituationInfo); subjectData = unknownSubjectData; unknownSubjectDatas.Add(stockSubjectId, unknownSubjectData); expBodiesSituationsBiomesSubject.AddSubject(subjectData.ExpInfo, bodyIndex, scienceSituation, biomeIndex, subjectData); } return(subjectData); }
public static bool IsVirtualBiomesRelevantForExperiment(this ScienceSituation situation, ExperimentInfo experiment) { return((experiment.VirtualBiomeMask & situation.BitValue()) != 0); }
private static bool MaskHasSituation(uint mask, ScienceSituation situation) { return((mask & situation.BitValue()) != 0); }
public ExperimentInfo(ScienceExperiment stockDef, ConfigNode expInfoNode) { // if we have a custom "KERBALISM_EXPERIMENT" definition for the experiment, load it, else just use an empty node to avoid nullrefs if (expInfoNode == null) { expInfoNode = new ConfigNode(); } this.stockDef = stockDef; ExperimentId = stockDef.id; // We have some custom handling for breaking ground ROC experiments IsROC = ExperimentId.StartsWith("ROCScience"); if (IsROC) { Title = "ROC: " + stockDef.experimentTitle; // group ROC together in the science archive (sorted by Title) } else { Title = stockDef.experimentTitle; } // A new bool field was added in 1.7 for serenity : applyScienceScale // if not specified, the default is `true`, which is the case for all non-serenity science defs // serenity ground experiments and ROCs have applyScienceScale = false. // for ground experiment, baseValue = science generated per hour // for ROC experiments, it doesn't change anything because they are all configured with baseValue = scienceCap if (this.stockDef.applyScienceScale) { DataSize = this.stockDef.baseValue * this.stockDef.dataScale; } else { DataSize = this.stockDef.scienceCap * this.stockDef.dataScale; } // load the included experiments ids in a string array, we will populate the list after // all ExperimentInfos are created. (can't do it here as they may not exist yet) includedExperimentsId = expInfoNode.GetValues("IncludeExperiment"); UnlockResourceSurvey = Lib.ConfigValue(expInfoNode, "UnlockResourceSurvey", false); SampleMass = Lib.ConfigValue(expInfoNode, "SampleMass", 0.0); IsSample = SampleMass > 0.0; if (IsSample) { // make sure we don't produce NaN values down the line because of odd/wrong configs if (DataSize <= 0.0) { Lib.Log(ExperimentId + " has DataSize=" + DataSize + ", your configuration is broken!", Lib.LogLevel.Warning); DataSize = 1.0; } MassPerMB = SampleMass / DataSize; } else { MassPerMB = 0.0; } // Patch stock science def restrictions as BodyAllowed/BodyNotAllowed restrictions if (!(expInfoNode.HasValue("BodyAllowed") || expInfoNode.HasValue("BodyNotAllowed"))) { if (IsROC) { // Parse the ROC definition name to find which body it's available on // This rely on the ROC definitions having the body name in the ExperimentId foreach (CelestialBody body in FlightGlobals.Bodies) { if (ExperimentId.IndexOf(body.name, StringComparison.OrdinalIgnoreCase) != -1) { expInfoNode.AddValue("BodyAllowed", body.name); break; } } } // parse the stock atmosphere restrictions into our own if (stockDef.requireAtmosphere) { expInfoNode.AddValue("BodyAllowed", "Atmospheric"); } else if (stockDef.requireNoAtmosphere) { expInfoNode.AddValue("BodyNotAllowed", "Atmospheric"); } } ExpBodyConditions = new BodyConditions(expInfoNode); foreach (string virtualBiomeStr in expInfoNode.GetValues("VirtualBiome")) { if (Enum.IsDefined(typeof(VirtualBiome), virtualBiomeStr)) { VirtualBiomes.Add((VirtualBiome)Enum.Parse(typeof(VirtualBiome), virtualBiomeStr)); } else { Lib.Log("Experiment definition `{0}` has unknown VirtualBiome={1}", Lib.LogLevel.Warning, ExperimentId, virtualBiomeStr); } } IgnoreBodyRestrictions = Lib.ConfigValue(expInfoNode, "IgnoreBodyRestrictions", false); uint situationMask = 0; uint biomeMask = 0; uint virtualBiomeMask = 0; // if defined, override stock situation / biome mask if (expInfoNode.HasValue("Situation")) { foreach (string situation in expInfoNode.GetValues("Situation")) { string[] sitAtBiome = situation.Split(new char[] { '@' }, StringSplitOptions.RemoveEmptyEntries); if (sitAtBiome.Length == 0 || sitAtBiome.Length > 2) { continue; } ScienceSituation scienceSituation = ScienceSituationUtils.ScienceSituationDeserialize(sitAtBiome[0]); if (scienceSituation != ScienceSituation.None) { situationMask += scienceSituation.BitValue(); if (sitAtBiome.Length == 2) { if (sitAtBiome[1].Equals("Biomes", StringComparison.OrdinalIgnoreCase)) { biomeMask += scienceSituation.BitValue(); } else if (sitAtBiome[1].Equals("VirtualBiomes", StringComparison.OrdinalIgnoreCase) && VirtualBiomes.Count > 0) { virtualBiomeMask += scienceSituation.BitValue(); } } } else { Lib.Log("Experiment definition `{0}` has unknown situation : `{1}`", Lib.LogLevel.Warning, ExperimentId, sitAtBiome[0]); } } } else { situationMask = stockDef.situationMask; biomeMask = stockDef.biomeMask; } if (situationMask == 0) { Lib.Log("Experiment definition `{0}` : `0` situationMask is unsupported, patching to `BodyGlobal`", Lib.LogLevel.Message, ExperimentId); situationMask = ScienceSituation.BodyGlobal.BitValue(); HasDBSubjects = false; } else { HasDBSubjects = !Lib.ConfigValue(expInfoNode, "IsGeneratingSubjects", false); } string error; uint stockSituationMask; uint stockBiomeMask; if (!ScienceSituationUtils.ValidateSituationBitMask(ref situationMask, biomeMask, out stockSituationMask, out stockBiomeMask, out error)) { Lib.Log("Experiment definition `{0}` is incorrect :\n{1}", Lib.LogLevel.Error, ExperimentId, error); } SituationMask = situationMask; BiomeMask = biomeMask; VirtualBiomeMask = virtualBiomeMask; stockDef.situationMask = stockSituationMask; stockDef.biomeMask = stockBiomeMask; }
public static bool IsAvailableForExperiment(this ScienceSituation situation, ExperimentInfo experiment) { return((experiment.SituationMask & situation.BitValue()) != 0); }