public static void Update(Vessel v, Vessel_info vi, VesselData vd, Resource_info ec, double elapsed_s) { // consume ec for internal transmitters (control and telemetry) ec.Consume(vi.connection.internal_cost * elapsed_s); // consume ec for external transmitters (don't consume for RemoteTech when loaded) if (!(RemoteTech.Enabled && v.loaded)) { ec.Consume(vi.connection.external_cost * elapsed_s); } // do nothing if network is not ready if (!NetworkInitialized) { return; } // maintain and send messages // - do not send messages during/after solar storms // - do not send messages for EVA kerbals if (!v.isEVA && v.situation != Vessel.Situations.PRELAUNCH) { if (!vd.msg_signal && !vi.connection.linked) { vd.msg_signal = true; if (vd.cfg_signal) { string subtext = Localizer.Format("#KERBALISM_UI_transmissiondisabled"); switch (vi.connection.status) { case LinkStatus.plasma: subtext = Localizer.Format("#KERBALISM_UI_Plasmablackout"); break; case LinkStatus.storm: subtext = Localizer.Format("#KERBALISM_UI_Stormblackout"); break; default: if (vi.crew_count == 0) { switch (Settings.UnlinkedControl) { case UnlinkedCtrl.none: subtext = Localizer.Format("#KERBALISM_UI_noctrl"); break; case UnlinkedCtrl.limited: subtext = Localizer.Format("#KERBALISM_UI_limitedcontrol"); break; } } break; } Message.Post(Severity.warning, Lib.BuildString(Localizer.Format("#KERBALISM_UI_signallost"), " <b>", v.vesselName, "</b>"), subtext); } } else if (vd.msg_signal && vi.connection.linked) { vd.msg_signal = false; if (vd.cfg_signal) { Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> ", Localizer.Format("#KERBALISM_UI_signalback")), vi.connection.status == LinkStatus.direct_link ? Localizer.Format("#KERBALISM_UI_directlink") : Lib.BuildString(Localizer.Format("#KERBALISM_UI_relayby"), " <b>", vi.connection.target_name, "</b>")); } } } }
public static void update(Vessel v, vessel_info vi, VesselData vd, vessel_resources resources, double elapsed_s) { // get most used resource handlers resource_info ec = resources.Info(v, "ElectricCharge"); // store data required to support multiple modules of same type in a part var PD = new Dictionary <string, Lib.module_prefab_data>(); // for each part foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; // get all module prefabs var module_prefabs = part_prefab.FindModulesImplementing <PartModule>(); // clear module indexes PD.Clear(); // for each module foreach (ProtoPartModuleSnapshot m in p.modules) { // get module type // if the type is unknown, skip it module_type type = ModuleType(m.moduleName); if (type == module_type.Unknown) { continue; } // get the module prefab // if the prefab doesn't contain this module, skip it PartModule module_prefab = Lib.ModulePrefab(module_prefabs, m.moduleName, PD); if (!module_prefab) { continue; } // if the module is disabled, skip it // note: this must be done after ModulePrefab is called, so that indexes are right if (!Lib.Proto.GetBool(m, "isEnabled")) { continue; } // process modules // note: this should be a fast switch, possibly compiled to a jump table switch (type) { case module_type.Reliability: Reliability.BackgroundUpdate(v, p, m, module_prefab as Reliability); break; case module_type.Experiment: Experiment.BackgroundUpdate(v, m, module_prefab as Experiment, ec, elapsed_s); break; case module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, m, module_prefab as Greenhouse, vi, resources, elapsed_s); break; case module_type.GravityRing: GravityRing.BackgroundUpdate(v, p, m, module_prefab as GravityRing, ec, elapsed_s); break; case module_type.Emitter: Emitter.BackgroundUpdate(v, p, m, module_prefab as Emitter, ec, elapsed_s); break; case module_type.Harvester: Harvester.BackgroundUpdate(v, m, module_prefab as Harvester, elapsed_s); break; case module_type.Laboratory: Laboratory.BackgroundUpdate(v, p, m, module_prefab as Laboratory, ec, elapsed_s); break; case module_type.Command: ProcessCommand(v, p, m, module_prefab as ModuleCommand, resources, elapsed_s); break; case module_type.Panel: ProcessPanel(v, p, m, module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break; case module_type.Generator: ProcessGenerator(v, p, m, module_prefab as ModuleGenerator, resources, elapsed_s); break; case module_type.Converter: ProcessConverter(v, p, m, module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case module_type.Drill: ProcessHarvester(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; case module_type.AsteroidDrill: ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; case module_type.StockLab: ProcessStockLab(v, p, m, module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case module_type.Light: ProcessLight(v, p, m, module_prefab as ModuleLight, ec, elapsed_s); break; case module_type.Scanner: ProcessScanner(v, p, m, module_prefab, part_prefab, vd, ec, elapsed_s); break; case module_type.CurvedPanel: ProcessCurvedPanel(v, p, m, module_prefab, part_prefab, vi, ec, elapsed_s); break; case module_type.FissionGenerator: ProcessFissionGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case module_type.CryoTank: ProcessCryoTank(v, p, m, module_prefab, resources, elapsed_s); break; } } } }
// consume EC for transmission, and transmit science data public static void Update(Vessel v, VesselData vd, ResourceInfo ec, double elapsed_s) { // do nothing if science system is disabled if (!Features.Science) { return; } // consume ec for transmitters ec.Consume(vd.Connection.ec_idle * elapsed_s, ResourceBroker.CommsIdle); // avoid corner-case when RnD isn't live during scene changes // - this avoid losing science if the buffer reach threshold during a scene change if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX && ResearchAndDevelopment.Instance == null) { return; } // clear list of files transmitted vd.filesTransmitted.Clear(); // check connection if (vd.Connection == null || !vd.Connection.linked || vd.Connection.rate <= 0.0 || !vd.deviceTransmit || ec.Amount < vd.Connection.ec_idle * elapsed_s) { // reset all files transmit rate foreach (Drive drive in Drive.GetDrives(vd, true)) { foreach (File f in drive.files.Values) { f.transmitRate = 0.0; } } // do nothing else return; } double totalTransmitCapacity = vd.Connection.rate * elapsed_s; double remainingTransmitCapacity = totalTransmitCapacity; double scienceCredited = 0.0; GetFilesToTransmit(v, vd); if (xmitFiles.Count == 0) { return; } UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Science.Update-Loop"); // traverse the list in reverse because : // - warp cache files are at the end, and they are always transmitted regerdless of transmit capacity // - others files are next, sorted in science value per MB ascending order for (int i = xmitFiles.Count - 1; i >= 0; i--) { XmitFile xmitFile = xmitFiles[i]; if (xmitFile.file.size == 0.0) { continue; } // always transmit everything in the warp cache if (!xmitFile.isInWarpCache && remainingTransmitCapacity <= 0.0) { break; } // determine how much data is transmitted double transmitted = xmitFile.isInWarpCache ? xmitFile.file.size : Math.Min(xmitFile.file.size, remainingTransmitCapacity); if (transmitted == 0.0) { continue; } // consume transmit capacity remainingTransmitCapacity -= transmitted; // get science value double xmitScienceValue = transmitted * xmitFile.sciencePerMB; // consume data in the file xmitFile.file.size -= transmitted; // remove science collected (ignoring final science value clamped to subject completion) xmitFile.file.subjectData.RemoveScienceCollectedInFlight(xmitScienceValue); // fire subject completed events int timesCompleted = xmitFile.file.subjectData.UpdateSubjectCompletion(xmitScienceValue); if (timesCompleted > 0) { SubjectXmitCompleted(xmitFile.file, timesCompleted, v); } // save transmit rate for the file, and add it to the VesselData list of files being transmitted if (xmitFile.isInWarpCache && xmitFile.realDriveFile != null) { xmitFile.realDriveFile.transmitRate = transmitted / elapsed_s; vd.filesTransmitted.Add(xmitFile.realDriveFile); } else { xmitFile.file.transmitRate = transmitted / elapsed_s; vd.filesTransmitted.Add(xmitFile.file); } // clamp science value to subject max value xmitScienceValue = Math.Min(xmitScienceValue, xmitFile.file.subjectData.ScienceRemainingToRetrieve); if (xmitScienceValue > 0.0) { // add credits scienceCredited += xmitScienceValue; // credit the subject xmitFile.file.subjectData.AddScienceToRnDSubject(xmitScienceValue); } } UnityEngine.Profiling.Profiler.EndSample(); vd.scienceTransmitted += scienceCredited; // consume EC cost for transmission (ec_idle is consumed above) double transmittedCapacity = totalTransmitCapacity - remainingTransmitCapacity; double transmissionCost = (vd.Connection.ec - vd.Connection.ec_idle) * (transmittedCapacity / vd.Connection.rate); ec.Consume(transmissionCost, ResourceBroker.CommsXmit); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Science.Update-AddScience"); // Add science points, but wait until we have at least 0.1 points to add because AddScience is VERY slow // We don't use "TransactionReasons.ScienceTransmission" because AddScience fire multiple events not meant to be fired continuously // this avoid many side issues (ex : chatterer transmit sound playing continously, strategia "+0.0 science" popup...) ScienceDB.uncreditedScience += scienceCredited; if (ScienceDB.uncreditedScience > 0.1) { if (GameHasRnD) { ResearchAndDevelopment.Instance.AddScience((float)ScienceDB.uncreditedScience, TransactionReasons.None); } ScienceDB.uncreditedScience = 0.0; } UnityEngine.Profiling.Profiler.EndSample(); }
public void Execute(Vessel v, VesselData vd, VesselResources resources, double elapsed_s) { // store list of crew to kill List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>(); // get input resource handler ResourceInfo res = input.Length > 0 ? resources.GetResource(v, input) : null; // determine message variant uint variant = vd.EnvTemperature < Settings.LifeSupportSurvivalTemperature ? 0 : 1u; // get product of all environment modifiers double k = Modifiers.Evaluate(v, vd, resources, modifiers); bool lifetime_enabled = PreferencesRadiation.Instance.lifetime; // for each crew foreach (ProtoCrewMember c in Lib.CrewList(v)) { // get kerbal data KerbalData kd = DB.Kerbal(c.name); // skip rescue kerbals if (kd.rescue) { continue; } // skip disabled kerbals if (kd.disabled) { continue; } // get kerbal property data from db RuleData rd = kd.Rule(name); rd.lifetime = lifetime_enabled && lifetime; // influence consumption by elapsed time double step = elapsed_s; // if interval-based if (interval > 0.0) { // accumulate time rd.time_since += elapsed_s; // determine number of intervals that has passed (can be 2 or more if elapsed_s > interval * 2) step = Math.Floor(rd.time_since / interval); // consume time rd.time_since -= step * interval; } // if there is a resource specified if (res != null && rate > double.Epsilon) { // get rate including per-kerbal variance double resRate = rate // consumption rate * Variance(name, c, individuality) // kerbal-specific variance * k; // product of environment modifiers // determine amount of resource to consume double required = resRate * step; // seconds elapsed or interval amount // remember if a meal is consumed/produced in this simulation step if (interval > 0.0) { double ratePerStep = resRate / interval; res.UpdateIntervalRule(-required, -ratePerStep, name); if (output.Length > 0) { ResourceCache.GetResource(v, output).UpdateIntervalRule(required * ratio, ratePerStep * ratio, name); } } // if continuous, or if one or more intervals elapsed if (step > 0.0) { // if there is no output if (output.Length == 0) { // simply consume (that is faster) res.Consume(required, name); } // if there is an output else { // transform input into output resource // - rules always dump excess overboard (because it is waste) ResourceRecipe recipe = new ResourceRecipe(name); recipe.AddInput(input, required); recipe.AddOutput(output, required * ratio, true); resources.AddRecipe(recipe); } } } // if continuous, or if one or more intervals elapsed if (step > 0.0) { // degenerate: // - if the environment modifier is not telling to reset (by being zero) // - if this rule is resource-less, or if there was not enough resource in the vessel if (k > 0.0 && (input.Length == 0 || res.Amount <= double.Epsilon)) { rd.problem += degeneration // degeneration rate per-second or per-interval * k // product of environment modifiers * step // seconds elapsed or by number of steps * Variance(name, c, variance); // kerbal-specific variance } // else slowly recover else { rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002); } } bool do_breakdown = false; if (breakdown) { // don't do breakdowns and don't show stress message if disabled if (!PreferencesComfort.Instance.stressBreakdowns) { return; } // stress level double breakdown_probability = rd.problem / warning_threshold; breakdown_probability = Lib.Clamp(breakdown_probability, 0.0, 1.0); // use the stupidity of a kerbal. // however, nobody is perfect - not even a kerbal with a stupidity of 0. breakdown_probability *= c.stupidity * 0.6 + 0.4; // apply the weekly error rate breakdown_probability *= PreferencesComfort.Instance.stressBreakdownRate; // now we have the probability for one failure per week, based on the // individual stupidity and stress level of the kerbal. breakdown_probability = (breakdown_probability * elapsed_s) / (Lib.DaysInYear * Lib.HoursInDay * 3600); if (breakdown_probability > Lib.RandomDouble()) { do_breakdown = true; // we're stressed out and just made a major mistake, this further increases the stress level... rd.problem += warning_threshold * 0.05; // add 5% of the warning treshold to current stress level } } // kill kerbal if necessary if (rd.problem >= fatal_threshold) { #if DEBUG || DEVBUILD Lib.Log("Rule " + name + " kills " + c.name + " at " + rd.problem + " " + degeneration + "/" + k + "/" + step + "/" + Variance(name, c, variance)); #endif if (fatal_message.Length > 0) { Message.Post(breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(fatal_message, v, c, variant)); } if (breakdown) { do_breakdown = true; // move back between warning and danger level rd.problem = (warning_threshold + danger_threshold) * 0.5; // make sure next danger message is shown rd.message = 1; } else { deferred_kills.Add(c); } } // show messages else if (rd.problem >= danger_threshold && rd.message < 2) { if (danger_message.Length > 0) { Message.Post(Severity.danger, Lib.ExpandMsg(danger_message, v, c, variant)); } rd.message = 2; } else if (rd.problem >= warning_threshold && rd.message < 1) { if (warning_message.Length > 0) { Message.Post(Severity.warning, Lib.ExpandMsg(warning_message, v, c, variant)); } rd.message = 1; } else if (rd.problem < warning_threshold && rd.message > 0) { if (relax_message.Length > 0) { Message.Post(Severity.relax, Lib.ExpandMsg(relax_message, v, c, variant)); } rd.message = 0; } if (do_breakdown) { // trigger breakdown event Misc.Breakdown(v, c); } } // execute the deferred kills foreach (ProtoCrewMember c in deferred_kills) { Misc.Kill(v, c); } }
static void ProcessScanner(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule scanner, Part part_prefab, VesselData vd, resource_info ec, double elapsed_s) { // get ec consumption rate double power = SCANsat.EcConsumption(scanner); // if the scanner doesn't require power to operate, we aren't interested in simulating it if (power <= double.Epsilon) { return; } // get scanner state bool is_scanning = Lib.Proto.GetBool(m, "scanning"); // if its scanning if (is_scanning) { // consume ec ec.Consume(power * elapsed_s); // if there isn't ec // - comparing against amount in previous simulation step if (ec.amount <= double.Epsilon) { // unregister scanner SCANsat.stopScanner(v, m, part_prefab); is_scanning = false; // remember disabled scanner vd.scansat_id.Add(p.flightID); // give the user some feedback if (vd.cfg_ec) { Message.Post(Lib.BuildString("SCANsat sensor was disabled on <b>", v.vesselName, "</b>")); } } } // if it was disabled in background else if (vd.scansat_id.Contains(p.flightID)) { // if there is enough ec // note: comparing against amount in previous simulation step if (ec.level > 0.25) //< re-enable at 25% EC { // re-enable the scanner SCANsat.resumeScanner(v, m, part_prefab); is_scanning = true; // give the user some feedback if (vd.cfg_ec) { Message.Post(Lib.BuildString("SCANsat sensor resumed operations on <b>", v.vesselName, "</b>")); } } } // forget active scanners if (is_scanning) { vd.scansat_id.Remove(p.flightID); } }
bool Render_Vessel(Panel p, Vessel v) { // get vessel info Vessel_Info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) { return(false); } if (!Lib.IsVessel(v)) { return(false); } // get data from db VesselData vd = DB.Vessel(v); // determine if filter must be shown show_filter |= vd.group.Length > 0 && vd.group != "NONE"; // skip filtered vessels if (Filtered() && vd.group != filter) { return(false); } // get resource handler Vessel_Resources resources = ResourceCache.Get(v); // get vessel crew List <ProtoCrewMember> crew = Lib.CrewList(v); // get vessel name string vessel_name = v.isEVA ? crew[0].name : v.vesselName; // get body name string body_name = v.mainBody.name.ToUpper(); // render entry p.SetHeader ( Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, 20), "</b> <size=9><color=#cccccc>", Lib.Ellipsis(body_name, 8), "</color></size>"), string.Empty, () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; } ); // problem indicator Indicator_Problems(p, v, vi, crew); // battery indicator Indicator_EC(p, v, vi); // supply indicator if (Features.Supplies) { Indicator_Supplies(p, v, vi); } // reliability indicator if (Features.Reliability) { Indicator_Reliability(p, v, vi); } // signal indicator if (Features.Signal || Features.KCommNet || RemoteTech.Enabled()) { Indicator_Signal(p, v, vi); } // done return(true); }
public static void Update(Vessel v, VesselData vd, VesselResources resources, double elapsed_s) { if (!Lib.IsVessel(v)) { return; } // get most used resource handlers ResourceInfo ec = resources.GetResource(v, "ElectricCharge"); List <ResourceInfo> allResources = resources.GetAllResources(v); Dictionary <string, double> availableResources = new Dictionary <string, double>(); foreach (var ri in allResources) { availableResources[ri.ResourceName] = ri.Amount; } List <KeyValuePair <string, double> > resourceChangeRequests = new List <KeyValuePair <string, double> >(); foreach (var e in Background_PMs(v)) { switch (e.type) { case Module_type.Reliability: Reliability.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Reliability, elapsed_s); break; case Module_type.Experiment: (e.module_prefab as Experiment).BackgroundUpdate(v, vd, e.m, ec, resources, elapsed_s); break; // experiments use the prefab as a singleton instead of a static method case Module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, e.m, e.module_prefab as Greenhouse, vd, resources, elapsed_s); break; case Module_type.GravityRing: GravityRing.BackgroundUpdate(v, e.p, e.m, e.module_prefab as GravityRing, ec, elapsed_s); break; case Module_type.Harvester: Harvester.BackgroundUpdate(v, e.m, e.module_prefab as Harvester, elapsed_s); break; // Kerbalism ground and air harvester module case Module_type.Laboratory: Laboratory.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Laboratory, ec, elapsed_s); break; case Module_type.Command: ProcessCommand(v, e.p, e.m, e.module_prefab as ModuleCommand, resources, elapsed_s); break; case Module_type.Generator: ProcessGenerator(v, e.p, e.m, e.module_prefab as ModuleGenerator, resources, elapsed_s); break; case Module_type.Converter: ProcessConverter(v, e.p, e.m, e.module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case Module_type.Drill: ProcessDrill(v, e.p, e.m, e.module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; // Stock ground harvester module // case Module_type.AsteroidDrill: ProcessAsteroidDrill(v, e.p, e.m, e.module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; // Stock asteroid harvester module case Module_type.StockLab: ProcessStockLab(v, e.p, e.m, e.module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case Module_type.Light: ProcessLight(v, e.p, e.m, e.module_prefab as ModuleLight, ec, elapsed_s); break; case Module_type.Scanner: KerbalismScansat.BackgroundUpdate(v, e.p, e.m, e.module_prefab as KerbalismScansat, e.part_prefab, vd, ec, elapsed_s); break; case Module_type.FissionGenerator: ProcessFissionGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.CryoTank: ProcessCryoTank(v, e.p, e.m, e.module_prefab, resources, ec, elapsed_s); break; case Module_type.FNGenerator: ProcessFNGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.SolarPanelFixer: SolarPanelFixer.BackgroundUpdate(v, e.m, e.module_prefab as SolarPanelFixer, vd, ec, elapsed_s); break; case Module_type.KerbalismSentinel: KerbalismSentinel.BackgroundUpdate(v, e.m, e.module_prefab as KerbalismSentinel, vd, ec, elapsed_s); break; case Module_type.APIModule: ProcessApiModule(v, e.p, e.m, e.part_prefab, e.module_prefab, resources, availableResources, resourceChangeRequests, elapsed_s); break; } } }
// consume EC for transmission, and transmit science data public static void Update(Vessel v, Vessel_info vi, VesselData vd, Vessel_resources resources, double elapsed_s) { // do nothing if science system is disabled if (!Features.Science) { return; } // avoid corner-case when RnD isn't live during scene changes // - this avoid losing science if the buffer reach threshold during a scene change if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX && ResearchAndDevelopment.Instance == null) { return; } // get connection info ConnectionInfo conn = vi.connection; if (conn == null || String.IsNullOrEmpty(vi.transmitting)) { return; } // get filename of data being downloaded var exp_filename = vi.transmitting; var drive = FindDrive(vd, exp_filename); // if some data is being downloaded // - avoid cornercase at scene changes if (exp_filename.Length > 0 && drive != null) { // get file File file = drive.files[exp_filename]; // determine how much data is transmitted double transmitted = Math.Min(file.size, conn.rate * elapsed_s); // consume data in the file file.size -= transmitted; // accumulate in the buffer file.buff += transmitted; // if buffer is full, or file was transmitted completely if (file.size <= double.Epsilon || file.buff > buffer_capacity) { // collect the science data Credit(exp_filename, file.buff, true, v.protoVessel); // reset the buffer file.buff = 0.0; } // if file was transmitted completely if (file.size <= double.Epsilon) { // remove the file drive.files.Remove(exp_filename); // same file on another drive? drive = FindDrive(vd, exp_filename); if (!file.silentTransmission && drive == null) { // inform the user Message.Post( Lib.BuildString("<color=cyan><b>DATA RECEIVED</b></color>\nTransmission of <b>", Experiment(exp_filename).name, "</b> completed"), Lib.TextVariant("Our researchers will jump on it right now", "The checksum is correct, data must be valid")); } } } }
public static void Load(ConfigNode node) { // get version (or use current one for new savegames) string versionStr = Lib.ConfigValue(node, "version", Lib.KerbalismVersion.ToString()); // sanitize old saves (pre 3.1) format (X.X.X.X) to new format (X.X) if (versionStr.Split('.').Length > 2) { versionStr = versionStr.Split('.')[0] + "." + versionStr.Split('.')[1]; } version = new Version(versionStr); // if this is an unsupported version, print warning if (version <= new Version(1, 2)) { Lib.Log("loading save from unsupported version " + version); } // get unique id (or generate one for new savegames) uid = Lib.ConfigValue(node, "uid", Lib.RandomInt(int.MaxValue)); // load kerbals data kerbals = new Dictionary <string, KerbalData>(); if (node.HasNode("kerbals")) { foreach (var kerbal_node in node.GetNode("kerbals").GetNodes()) { kerbals.Add(From_safe_key(kerbal_node.name), new KerbalData(kerbal_node)); } } // load the science database, has to be before vessels are loaded ScienceDB.Load(node); UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.DB.Load.Vessels"); vessels.Clear(); // flightstate will be null when first creating the game if (HighLogic.CurrentGame.flightState != null) { ConfigNode vesselsNode = node.GetNode("vessels2"); if (vesselsNode == null) { vesselsNode = new ConfigNode(); } // HighLogic.CurrentGame.flightState.protoVessels is what is used by KSP to persist vessels // It is always available and synchronized in OnLoad, no matter the scene, excepted on the first OnLoad in a new game foreach (ProtoVessel pv in HighLogic.CurrentGame.flightState.protoVessels) { if (pv.vesselID == Guid.Empty) { // It seems flags are saved with an empty GUID. skip them. Lib.LogDebug("Skipping VesselData load for vessel with empty GUID :" + pv.vesselName); continue; } VesselData vd = new VesselData(pv, vesselsNode.GetNode(pv.vesselID.ToString())); vessels.Add(pv.vesselID, vd); Lib.LogDebug("VesselData loaded for vessel " + pv.vesselName); } } UnityEngine.Profiling.Profiler.EndSample(); // for compatibility with old saves, convert drives data (it's now saved in PartData) if (node.HasNode("drives")) { Dictionary <uint, PartData> allParts = new Dictionary <uint, PartData>(); foreach (VesselData vesselData in vessels.Values) { foreach (PartData partData in vesselData.PartDatas) { // we had a case of someone having a save with multiple parts having the same flightID // 5 duplicates, all were asteroids. if (!allParts.ContainsKey(partData.FlightId)) { allParts.Add(partData.FlightId, partData); } } } foreach (var drive_node in node.GetNode("drives").GetNodes()) { uint driveId = Lib.Parse.ToUInt(drive_node.name); if (allParts.ContainsKey(driveId)) { allParts[driveId].Drive = new Drive(drive_node); } } } // load bodies data storms = new Dictionary <string, StormData>(); if (node.HasNode("bodies")) { foreach (var body_node in node.GetNode("bodies").GetNodes()) { storms.Add(From_safe_key(body_node.name), new StormData(body_node)); } } // load landmark data if (node.HasNode("landmarks")) { landmarks = new LandmarkData(node.GetNode("landmarks")); } else { landmarks = new LandmarkData(); } // load ui data if (node.HasNode("ui")) { ui = new UIData(node.GetNode("ui")); } else { ui = new UIData(); } // if an old savegame was imported, log some debug info if (version != Lib.KerbalismVersion) { Lib.Log("savegame converted from version " + version + " to " + Lib.KerbalismVersion); } }
private static double HabitatRadiation(VesselData vd) { return((1.0 - vd.Shielding) * vd.EnvHabitatRadiation); }