void VesselDestroyed(Vessel v) { DB.vessels.Remove(Lib.VesselID(v)); // rescan the damn kerbals // - vessel crew is empty at destruction time // - we can't even use the flightglobal roster, because sometimes it isn't updated yet at this point HashSet <string> kerbals_alive = new HashSet <string>(); HashSet <string> kerbals_dead = new HashSet <string>(); foreach (Vessel ov in FlightGlobals.Vessels) { foreach (ProtoCrewMember c in Lib.CrewList(ov)) { kerbals_alive.Add(c.name); } } foreach (KeyValuePair <string, KerbalData> p in DB.Kerbals()) { if (!kerbals_alive.Contains(p.Key)) { kerbals_dead.Add(p.Key); } } foreach (string n in kerbals_dead) { // we don't know if the kerbal really is dead, or if it is just not currently assigned to a mission DB.KillKerbal(n, false); } // purge the caches ResourceCache.Purge(v); Drive.Purge(v); Cache.PurgeObjects(v); }
public static void Update(CelestialBody body, double elapsed_s) { // do nothing if storms are disabled if (!Features.SpaceWeather) return; // skip the sun if (body.flightGlobalsIndex == 0) return; // skip moons // note: referenceBody is never null here if (body.referenceBody.flightGlobalsIndex != 0) return; // get body data BodyData bd = DB.Body(body.name); // generate storm time if necessary if (bd.storm_time <= double.Epsilon) { bd.storm_time = PreferencesStorm.Instance.StormMinTime + (PreferencesStorm.Instance.StormMaxTime - PreferencesStorm.Instance.StormMinTime) * Lib.RandomDouble(); } // accumulate age bd.storm_age += elapsed_s * Storm_frequency(body.orbit.semiMajorAxis); // if storm is over if (bd.storm_age > bd.storm_time) { bd.storm_age = 0.0; bd.storm_time = 0.0; bd.storm_state = 0; } // if storm is in progress else if (bd.storm_age > bd.storm_time - PreferencesStorm.Instance.StormDuration) { bd.storm_state = 2; } // if storm is incoming else if (bd.storm_age > bd.storm_time - PreferencesStorm.Instance.StormDuration - Time_to_impact(body.orbit.semiMajorAxis)) { bd.storm_state = 1; } // send messages // note: separed from state management to support the case when the user enter the SOI of a body under storm or about to be hit if (bd.msg_storm < 2 && bd.storm_state == 2) { if (Body_is_relevant(body)) { Message.Post(Severity.danger, Lib.BuildString("The coronal mass ejection hit <b>", body.name, "</b> system"), Lib.BuildString("Storm duration: ", Lib.HumanReadableDuration(TimeLeftCME(bd.storm_time, bd.storm_age)))); } bd.msg_storm = 2; } else if (bd.msg_storm < 1 && bd.storm_state == 1) { if (Body_is_relevant(body)) { Message.Post(Severity.warning, Lib.BuildString("Our observatories report a coronal mass ejection directed toward <b>", body.name, "</b> system"), Lib.BuildString("Time to impact: ", Lib.HumanReadableDuration(TimeBeforeCME(bd.storm_time, bd.storm_age)))); } bd.msg_storm = 1; } else if (bd.msg_storm > 1 && bd.storm_state == 0) { if (Body_is_relevant(body)) { Message.Post(Severity.relax, Lib.BuildString("The solar storm at <b>", body.name, "</b> system is over")); } bd.msg_storm = 0; } }
// kill a kerbal // note: you can't kill a kerbal while iterating over vessel crew list, do it outside the loop public static void Kill(Vessel v, ProtoCrewMember c) { // if on pod if (!v.isEVA) { // if vessel is loaded if (v.loaded) { // find part Part part = null; foreach (Part p in v.parts) { if (p.protoModuleCrew.Find(k => k.name == c.name) != null) { part = p; break; } } // remove kerbal from part part.RemoveCrewmember(c); // and from vessel v.RemoveCrew(c); // then kill it c.Die(); } // if vessel is not loaded else { // find proto part ProtoPartSnapshot part = null; foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { if (p.HasCrew(c.name)) { part = p; break; } } // remove from part part.RemoveCrew(c.name); // and from vessel v.protoVessel.RemoveCrew(c); // flag as dead c.rosterStatus = ProtoCrewMember.RosterStatus.Dead; } // forget kerbal data DB.KillKerbal(c.name, true); } // else it must be an eva death else { // flag as eva death DB.Kerbal(c.name).eva_dead = true; // rename vessel v.vesselName = c.name + "'s body"; } // remove reputation if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER) { Reputation.Instance.AddReputation(-PreferencesBasic.Instance.deathPenalty, TransactionReasons.Any); } }
public override void OnLoad(ConfigNode node) { // deserialize data DB.Load(node); Communications.NetworkInitialized = false; Communications.NetworkInitializing = false; // initialize everything just once if (!initialized) { // add supply resources to pods Profile.SetupPods(); // initialize subsystems Cache.Init(); ResourceCache.Init(); Radiation.Init(); Science.Init(); LineRenderer.Init(); ParticleRenderer.Init(); Highlighter.Init(); UI.Init(); // prepare storm data foreach (CelestialBody body in FlightGlobals.Bodies) { if (Storm.Skip_body(body)) { continue; } Storm_data sd = new Storm_data { body = body }; storm_bodies.Add(sd); } // various tweaks to the part icons in the editor Misc.TweakPartIcons(); // setup callbacks callbacks = new Callbacks(); // everything was initialized initialized = true; } // detect if this is a different savegame if (DB.uid != savegame_uid) { // clear caches Cache.Clear(); ResourceCache.Clear(); Message.all_logs.Clear(); // sync main window pos from db UI.Sync(); // remember savegame id savegame_uid = DB.uid; } }
void FixedUpdate() { // remove control locks in any case Misc.ClearLocks(); // do nothing if paused if (Lib.IsPaused()) { return; } // maintain elapsed_s, converting to double only once // and detect warp blending double fixedDeltaTime = TimeWarp.fixedDeltaTime; if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon) { warp_blending = 0; } else { ++warp_blending; } elapsed_s = fixedDeltaTime; // evict oldest entry from vessel cache Cache.Update(); // store info for oldest unloaded vessel double last_time = 0.0; Vessel last_v = null; Vessel_info last_vi = null; VesselData last_vd = null; Vessel_resources last_resources = null; // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // get vessel info from the cache Vessel_info vi = Cache.VesselInfo(v); // set locks for active vessel if (v.isActiveVessel) { Misc.SetLocks(v, vi); } // maintain eva dead animation and helmet state if (v.loaded && v.isEVA) { EVA.Update(v); } // keep track of rescue mission kerbals, and gift resources to their vessels on discovery if (v.loaded && vi.is_vessel) { // manage rescue mission mechanics Misc.ManageRescueMission(v); } // do nothing else for invalid vessels if (!vi.is_valid) { continue; } // get vessel data from db VesselData vd = DB.Vessel(v); // get resource cache Vessel_resources resources = ResourceCache.Get(v); // if loaded if (v.loaded) { // get most used resource Resource_info ec = resources.Info(v, "ElectricCharge"); // show belt warnings Radiation.BeltWarnings(v, vi, vd); // update storm data Storm.Update(v, vi, vd, elapsed_s); Communications.Update(v, vi, vd, ec, elapsed_s); // Habitat equalization ResourceBalance.Equalizer(v); // transmit science data Science.Update(v, vi, vd, resources, elapsed_s); // apply rules Profile.Execute(v, vi, vd, resources, elapsed_s); // apply deferred requests resources.Sync(v, elapsed_s); // call automation scripts vd.computer.Automate(v, vi, resources); // remove from unloaded data container unloaded.Remove(vi.id); } // if unloaded else { // get unloaded data, or create an empty one Unloaded_data ud; if (!unloaded.TryGetValue(vi.id, out ud)) { ud = new Unloaded_data(); unloaded.Add(vi.id, ud); } // accumulate time ud.time += elapsed_s; // maintain oldest entry if (ud.time > last_time) { last_time = ud.time; last_v = v; last_vi = vi; last_vd = vd; last_resources = resources; } } } // at most one vessel gets background processing per physics tick // if there is a vessel that is not the currently loaded vessel, then // we will update the vessel whose most recent background update is the oldest if (last_v != null) { // get most used resource Resource_info last_ec = last_resources.Info(last_v, "ElectricCharge"); // show belt warnings Radiation.BeltWarnings(last_v, last_vi, last_vd); // update storm data Storm.Update(last_v, last_vi, last_vd, last_time); Communications.Update(last_v, last_vi, last_vd, last_ec, last_time); // transmit science data Science.Update(last_v, last_vi, last_vd, last_resources, last_time); // apply rules Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time); // simulate modules in background Background.Update(last_v, last_vi, last_vd, last_resources, last_time); // apply deferred requests last_resources.Sync(last_v, last_time); // call automation scripts last_vd.computer.Automate(last_v, last_vi, last_resources); // remove from unloaded data container unloaded.Remove(last_vi.id); } // update storm data for one body per-step if (storm_bodies.Count > 0) { storm_bodies.ForEach(k => k.time += elapsed_s); Storm_data sd = storm_bodies[storm_index]; Storm.Update(sd.body, sd.time); sd.time = 0.0; storm_index = (storm_index + 1) % storm_bodies.Count; } }
public override void OnSave(ConfigNode node) { // serialize data DB.Save(node); }
/// <summary> /// If short_strings parameter is true then the strings used for display of the data will be shorter when inflight. /// </summary> public static void Fileman(this Panel p, Vessel v, bool short_strings = false) { // avoid corner-case when this is called in a lambda after scene changes v = FlightGlobals.FindVessel(v.id); // if vessel doesn't exist anymore, leave the panel empty if (v == null) { return; } // get info from the cache Vessel_info vi = Cache.VesselInfo(v); // if not a valid vessel, leave the panel empty if (!vi.is_valid) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(40)), " <color=#cccccc>FILE MANAGER</color>")); p.Width(Styles.ScaleWidthFloat(465.0f)); p.paneltype = Panel.PanelType.data; // time-out simulation if (p.Timeout(vi)) { return; } foreach (var idDrivePair in DB.Vessel(v).drives) { var drive = idDrivePair.Value; if (drive.dataCapacity > double.Epsilon) { // draw data section p.AddSection(Lib.BuildString("DATA ", drive.name, " ", Lib.HumanReadableDataSize(drive.dataCapacity), " (", Lib.HumanReadablePerc(drive.FilesSize() / drive.dataCapacity), ")")); foreach (var pair in drive.files) { string filename = pair.Key; File file = pair.Value; Render_file(p, filename, file, drive, short_strings && Lib.IsFlight(), Cache.VesselInfo(v).connection.rate); } if (drive.files.Count == 0) { p.AddContent("<i>no files</i>", string.Empty); } } if (drive.sampleCapacity > 0) { double mass = 0; foreach (var sample in drive.samples.Values) { mass += sample.mass; } // draw samples section p.AddSection(Lib.BuildString("SAMPLES ", drive.name, " ", Lib.HumanReadableSampleSize(drive.sampleCapacity), " (", Lib.HumanReadablePerc(drive.SamplesSize() / drive.sampleCapacity), ") ", Lib.HumanReadableMass(mass))); foreach (var pair in drive.samples) { string filename = pair.Key; Sample sample = pair.Value; Render_sample(p, filename, sample, drive, short_strings && Lib.IsFlight()); } if (drive.samples.Count == 0) { p.AddContent("<i>no samples</i>", string.Empty); } } } }
// constructor /// <summary> Creates a <see cref="ConnectionInfo"/> object for the specified vessel from it's antenna modules</summary> public ConnectionInfo(Vessel v, bool powered, bool storm) { // set RemoteTech powered and storm state if (RemoteTech.Enabled) { RemoteTech.SetPoweredDown(v.id, !powered); RemoteTech.SetCommsBlackout(v.id, storm); } // return no connection if there is no ec left if (!powered) { // hysteresis delay if ((DB.Vessel(v).hyspos_signal >= 5.0)) { DB.Vessel(v).hyspos_signal = 5.0; DB.Vessel(v).hysneg_signal = 0.0; return; } DB.Vessel(v).hyspos_signal += 0.1; } else { // hysteresis delay DB.Vessel(v).hysneg_signal += 0.1; if (!(DB.Vessel(v).hysneg_signal >= 5.0)) { return; } DB.Vessel(v).hysneg_signal = 5.0; DB.Vessel(v).hyspos_signal = 0.0; } // CommNet or simple signal system if (!RemoteTech.Enabled) { List <ModuleDataTransmitter> transmitters; // if vessel is loaded if (v.loaded) { // find transmitters transmitters = v.FindPartModulesImplementing <ModuleDataTransmitter>(); if (transmitters != null) { foreach (ModuleDataTransmitter t in transmitters) { if (t.antennaType == AntennaType.INTERNAL) // do not include internal data rate, ec cost only { internal_cost += t.DataResourceCost * t.DataRate; } else { // do we have an animation ModuleDeployableAntenna animation = t.part.FindModuleImplementing <ModuleDeployableAntenna>(); if (animation != null) { // only include data rate and ec cost if transmitter is extended if (animation.deployState == ModuleDeployablePart.DeployState.EXTENDED) { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } // no animation else { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } } } } // if vessel is not loaded else { // find proto transmitters foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; transmitters = part_prefab.FindModulesImplementing <ModuleDataTransmitter>(); if (transmitters != null) { foreach (ModuleDataTransmitter t in transmitters) { if (t.antennaType == AntennaType.INTERNAL) // do not include internal data rate, ec cost only { internal_cost += t.DataResourceCost * t.DataRate; } else { // do we have an animation ProtoPartModuleSnapshot m = p.FindModule("ModuleDeployableAntenna"); if (m != null) { // only include data rate and ec cost if transmitter is extended string deployState = Lib.Proto.GetString(m, "deployState"); if (deployState == "EXTENDED") { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } // no animation else { rate += t.DataRate; external_cost += t.DataResourceCost * t.DataRate; } } } } } } // if CommNet is enabled if (HighLogic.fetch.currentGame.Parameters.Difficulty.EnableCommNet) { // are we connected to DSN if (v.connection != null) { if (v.connection.IsConnected) { linked = true; status = v.connection.ControlPath.First.hopType == CommNet.HopType.Home ? LinkStatus.direct_link : LinkStatus.indirect_link; strength = v.connection.SignalStrength; rate = rate * strength; target_name = Lib.Ellipsis(Localizer.Format(v.connection.ControlPath.First.end.displayName).Replace("Kerbin", "DSN"), 20); return; } // is loss of connection due to plasma blackout else if (Lib.ReflectionValue <bool>(v.connection, "inPlasma")) // calling InPlasma causes a StackOverflow :( { status = LinkStatus.plasma; rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } } // no connection rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } // the simple stupid always connected signal system linked = true; status = LinkStatus.direct_link; strength = 1; // 100 % target_name = "DSN: KSC"; return; } // RemoteTech signal system else { // if vessel is loaded if (v.loaded) { // find transmitters foreach (Part p in v.parts) { foreach (PartModule m in p.Modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { internal_cost += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include data rate and ec cost if transmitter is active if (Lib.ReflectionValue <bool>(m, "IsRTActive")) { rate += (Lib.ReflectionValue <float>(m, "RTPacketSize") / Lib.ReflectionValue <float>(m, "RTPacketInterval")); external_cost += m.resHandler.inputResources.Find(r => r.name == "ElectricCharge").rate; } } } } } // if vessel is not loaded else { // find proto transmitters foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; int index = 0; // module index foreach (ProtoPartModuleSnapshot m in p.modules) { // calculate internal (passive) transmitter ec usage @ 0.5W each if (m.moduleName == "ModuleRTAntennaPassive") { internal_cost += 0.0005; } // calculate external transmitters else if (m.moduleName == "ModuleRTAntenna") { // only include data rate and ec cost if transmitter is active skip if index is out of range if (Lib.Proto.GetBool(m, "IsRTActive") && index < part_prefab.Modules.Count) { // get module prefab PartModule pm = part_prefab.Modules.GetModule(index); if (pm != null) { rate += (Lib.ReflectionValue <float>(pm, "RTPacketSize") / Lib.ReflectionValue <float>(pm, "RTPacketInterval")); external_cost += pm.resHandler.inputResources.Find(r => r.name == "ElectricCharge").rate; } else { Lib.DebugLog(String.Format("ConnectionInfo: Could not find PartModule ModuleRTAntenna for part {0} on unloaded vessel {1}, using default values as a workaround", p.partName, v.vesselName)); rate += 6.6666; // 6.67 Mb/s external_cost += 0.025; // 25 W/s } } } index++; } } } // are we connected if (RemoteTech.Connected(v.id)) { linked = RemoteTech.ConnectedToKSC(v.id); status = RemoteTech.TargetsKSC(v.id) ? LinkStatus.direct_link : LinkStatus.indirect_link; strength = RemoteTech.GetSignalDelay(v.id); target_name = status == LinkStatus.direct_link ? Lib.Ellipsis("DSN: " + (RemoteTech.NameTargetsKSC(v.id) ?? ""), 20): Lib.Ellipsis(RemoteTech.NameFirstHopToKSC(v.id) ?? "", 20); return; } // is loss of connection due to a blackout else if (RemoteTech.GetCommsBlackout(v.id)) { status = storm ? LinkStatus.storm : LinkStatus.plasma; rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } // no connection rate = 0.0; internal_cost = 0.0; external_cost = 0.0; return; } }
void ToEVA(GameEvents.FromToAction <Part, Part> data) { Cache.PurgeObjects(data.from.vessel); Cache.PurgeObjects(data.to.vessel); // get total crew in the origin vessel double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0; // get vessel resources handler Vessel_resources resources = ResourceCache.Get(data.from.vessel); // setup supply resources capacity in the eva kerbal Profile.SetupEva(data.to); String prop_name = Lib.EvaPropellantName(); // for each resource in the kerbal for (int i = 0; i < data.to.Resources.Count; ++i) { // get the resource PartResource res = data.to.Resources[i]; // eva prop is handled differently if (res.resourceName == prop_name) { continue; } double quantity = Math.Min(resources.Info(data.from.vessel, res.resourceName).amount / tot_crew, res.maxAmount); // remove resource from vessel quantity = data.from.RequestResource(res.resourceName, quantity); // add resource to eva kerbal data.to.RequestResource(res.resourceName, -quantity); } // take as much of the propellant as possible. just imagine: there are 1.3 units left, and 12 occupants // in the ship. you want to send out an engineer to fix the chemical plant that produces monoprop, // and have to get from one end of the station to the other with just 0.1 units in the tank... // nope. double evaPropQuantity = data.from.RequestResource(prop_name, Lib.EvaPropellantCapacity()); // We can't just add the monoprop here, because that doesn't always work. It might be related // to the fact that stock KSP wants to add 5 units of monoprop to new EVAs. Instead of fighting KSP here, // we just let it do it's thing and set our amount later in EVA.cs - which seems to work just fine. // don't put that into Cache.VesselInfo because that can be deleted before we get there Cache.SetVesselObjectsCache(data.to.vessel, "eva_prop", evaPropQuantity); // Airlock loss resources.Consume(data.from.vessel, "Nitrogen", PreferencesLifeSupport.Instance.evaAtmoLoss, "airlock"); // show warning if there is little or no EVA propellant in the suit if (evaPropQuantity <= 0.05 && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", prop_name, "</b> in the EVA suit"), "Don't let the ladder go!"); } // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>(); EVA.HeadLamps(kerbal, false); // execute script DB.Vessel(data.from.vessel).computer.Execute(data.from.vessel, ScriptType.eva_out); }
public static void Devman(this Panel p, Vessel v) { // avoid corner-case when this is called in a lambda after scene changes v = FlightGlobals.FindVessel(v.id); // if vessel doesn't exist anymore, leave the panel empty if (v == null) { return; } // get info from the cache Vessel_info vi = Cache.VesselInfo(v); // if not a valid vessel, leave the panel empty if (!vi.is_valid) { return; } // set metadata p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, Styles.ScaleStringLength(20)), " <color=#cccccc>" + Localizer.Format("#KERBALISM_UI_devman") + "</color>")); p.Width(Styles.ScaleWidthFloat(355.0f)); p.paneltype = Panel.PanelType.scripts; // time-out simulation if (p.Timeout(vi)) { return; } // get devices Dictionary <uint, Device> devices = Computer.Boot(v); int deviceCount = 0; // direct control if (script_index == 0) { // draw section title and desc p.AddSection ( Localizer.Format("#KERBALISM_UI_devices"), Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last), true ); // for each device foreach (var pair in devices) { // render device entry Device dev = pair.Value; if (!dev.IsVisible()) { continue; } p.AddContent(dev.Name(), dev.Info(), string.Empty, dev.Toggle, () => Highlighter.Set(dev.Part(), Color.cyan)); deviceCount++; } } // script editor else { // get script ScriptType script_type = (ScriptType)script_index; string script_name = script_type.ToString().Replace('_', ' ').ToUpper(); Script script = DB.Vessel(v).computer.Get(script_type); // draw section title and desc p.AddSection ( script_name, Description(), () => p.Prev(ref script_index, (int)ScriptType.last), () => p.Next(ref script_index, (int)ScriptType.last), true ); // for each device foreach (var pair in devices) { Device dev = pair.Value; if (!dev.IsVisible()) { continue; } // determine tribool state int state = !script.states.ContainsKey(pair.Key) ? -1 : !script.states[pair.Key] ? 0 : 1; // render device entry p.AddContent ( dev.Name(), state == -1 ? "<color=#999999>" + Localizer.Format("#KERBALISM_UI_dontcare") + " </color>" : state == 0 ? "<color=red>" + Localizer.Format("#KERBALISM_Generic_OFF") + "</color>" : "<color=cyan>" + Localizer.Format("#KERBALISM_Generic_ON") + "</color>", string.Empty, () => { switch (state) { case -1: script.Set(dev, true); break; case 0: script.Set(dev, null); break; case 1: script.Set(dev, false); break; } }, () => Highlighter.Set(dev.Part(), Color.cyan) ); deviceCount++; } } // no devices case if (deviceCount == 0) { p.AddContent("<i>no devices</i>"); } }
public override void OnSave(ConfigNode node) { // serialize data Science.CreditAllDeferred(); DB.Save(node); }
public Drive(ConfigNode node) { // parse science files files = new Dictionary <SubjectData, File>(); if (node.HasNode("files")) { foreach (var file_node in node.GetNode("files").GetNodes()) { string subject_id = DB.From_safe_key(file_node.name); File file = File.Load(subject_id, file_node); if (file != null) { if (files.ContainsKey(file.subjectData)) { Lib.Log("Warning: discarding duplicate subject " + file.subjectData); } else { files.Add(file.subjectData, file); file.subjectData.AddDataCollectedInFlight(file.size); } } else { file = File.LoadOldFormat(subject_id, file_node); if (file != null) { Lib.Log("Drive file load : converted '" + subject_id + "' to new format"); if (files.ContainsKey(file.subjectData)) { Lib.Log("Warning: discarding duplicate converted subject " + file.subjectData); } else { files.Add(file.subjectData, file); file.subjectData.AddDataCollectedInFlight(file.size); } } } } } // parse science samples samples = new Dictionary <SubjectData, Sample>(); if (node.HasNode("samples")) { foreach (var sample_node in node.GetNode("samples").GetNodes()) { string subject_id = DB.From_safe_key(sample_node.name); Sample sample = Sample.Load(subject_id, sample_node); if (sample != null) { samples.Add(sample.subjectData, sample); sample.subjectData.AddDataCollectedInFlight(sample.size); } else { sample = Sample.LoadOldFormat(subject_id, sample_node); if (sample != null) { Lib.Log("Drive sample load : converted '" + subject_id + "' to new format"); samples.Add(sample.subjectData, sample); sample.subjectData.AddDataCollectedInFlight(sample.size); } } } } name = Lib.ConfigValue(node, "name", "DRIVE"); is_private = Lib.ConfigValue(node, "is_private", false); // parse capacities. be generous with default values for backwards // compatibility (drives had unlimited storage before this) dataCapacity = Lib.ConfigValue(node, "dataCapacity", 100000.0); sampleCapacity = Lib.ConfigValue(node, "sampleCapacity", 1000); fileSendFlags = new Dictionary <string, bool>(); string fileNames = Lib.ConfigValue(node, "sendFileNames", string.Empty); foreach (string fileName in Lib.Tokenize(fileNames, ',')) { Send(fileName, true); } }
public void TransferData() { DB.Vessel(vessel).drive.location = part.flightID; }
public void Update() { if (Lib.IsFlight()) { Drive drive = DB.Vessel(vessel).drive; // if no location was ever specified, set it here if (drive.location == 0) { drive.location = part.flightID; } // if this is the location the data is stored if (drive.location == part.flightID) { // get data size double size = drive.Size(); // show DATA UI button, with size info Events["ToggleUI"].guiName = Lib.StatusToggle("Data", size > double.Epsilon ? Lib.HumanReadableDataSize(size) : "empty"); Events["ToggleUI"].active = true; // show TakeData eva action button, if there is something to take Events["TakeData"].active = size > double.Epsilon; // show StoreData eva action button, if active vessel is an eva kerbal and there is something to store from it Vessel v = FlightGlobals.ActiveVessel; Events["StoreData"].active = v != null && v.isEVA && !EVA.IsDead(v) && DB.Vessel(v).drive.Size() > double.Epsilon; // hide TransferLocation button Events["TransferData"].active = false; } // if this is not the location the data is stored else { // hide DATA UI button Events["ToggleUI"].active = false; // hide EVA actions Events["TakeData"].active = false; Events["StoreData"].active = false; // show TransferData button Events["TransferData"].active = true; } } }
// called every simulation step void FixedUpdate() { // do nothing if paused if (Lib.IsPaused()) return; // do nothing in the editors and the menus if (!Lib.SceneIsGame()) return; // do nothing if db isn't ready if (!DB.Ready()) return; // get elapsed time double elapsed_s = Kerbalism.elapsed_s; // evict oldest entry from vessel cache cache.update(); // store info for oldest unloaded vessel double last_time = 0.0; Vessel last_v = null; vessel_info last_vi = null; vessel_data last_vd = null; vessel_resources last_resources = null; // for each vessel foreach(Vessel v in FlightGlobals.Vessels) { // get vessel info from the cache vessel_info vi = Cache.VesselInfo(v); // skip invalid vessels if (!vi.is_valid) continue; // get vessel data from db vessel_data vd = DB.VesselData(v.id); // get resource cache vessel_resources resources = ResourceCache.Get(v); // if loaded if (v.loaded) { // show belt warnings Radiation.beltWarnings(v, vi, vd); // update storm data storm.update(v, vi, vd, elapsed_s); // consume relay EC and show signal warnings signal.update(v, vi, vd, resources, elapsed_s * vi.time_dilation); // apply rules Rule.applyRules(v, vi, vd, resources, elapsed_s * vi.time_dilation); // apply deferred requests resources.Sync(v, elapsed_s); // update computer vd.computer.update(v, elapsed_s); // remove from unloaded data container unloaded.Remove(vi.id); } // if unloaded else { // get unloaded data, or create an empty one unloaded_data ud; if (!unloaded.TryGetValue(vi.id, out ud)) { ud = new unloaded_data(); unloaded.Add(vi.id, ud); } // accumulate time ud.time += elapsed_s; // maintain oldest entry if (ud.time > last_time) { last_time = ud.time; last_v = v; last_vi = vi; last_vd = vd; last_resources = resources; } } } // if the oldest unloaded vessel was selected if (last_v != null) { // show belt warnings Radiation.beltWarnings(last_v, last_vi, last_vd); // decay unloaded vessels inside atmosphere Kerbalism.atmosphereDecay(last_v, last_vi, last_time); // update storm data storm.update(last_v, last_vi, last_vd, last_time); // consume relay EC and show signal warnings signal.update(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation); // apply rules Rule.applyRules(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation); // simulate modules in background Background.update(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation); // apply deferred requests last_resources.Sync(last_v, last_time); // update computer last_vd.computer.update(last_v, last_time); // remove from unloaded data container unloaded.Remove(last_vi.id); } // update storm data for one body per-step storm_bodies.ForEach(k => k.time += elapsed_s); storm_data sd = storm_bodies[storm_index]; storm.update(sd.body, sd.time); sd.time = 0.0; storm_index = (storm_index + 1) % storm_bodies.Count; }
public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) { return; } // do nothing if vessel is invalid if (!Cache.VesselInfo(vessel).is_valid) { return; } // get ec handler Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // if experiment is active if (recording) { // detect conditions // - comparing against amount in previous step bool has_ec = ec.amount > double.Epsilon; bool has_operator = operator_cs.Check(vessel); string sit = Science.Situation(vessel, situations); // deduce issues issue = string.Empty; if (sit.Length == 0) { issue = "invalid situation"; } else if (!has_operator) { issue = "no operator"; } else if (!has_ec) { issue = "missing <b>EC</b>"; } // if there are no issues if (issue.Length == 0) { // generate subject id string subject_id = Science.Generate_subject(experiment, vessel.mainBody, sit, Science.Biome(vessel, sit), Science.Multiplier(vessel, sit)); // record in drive if (transmissible) { DB.Vessel(vessel).drive.Record_file(subject_id, data_rate * Kerbalism.elapsed_s); } else { DB.Vessel(vessel).drive.Record_sample(subject_id, data_rate * Kerbalism.elapsed_s); } // consume ec ec.Consume(ec_rate * Kerbalism.elapsed_s); } } }