public void Update() { if (drive == null) { return; } if (Lib.IsFlight()) { // show DATA UI button, with size info Events["ToggleUI"].guiName = Lib.StatusToggle("Data", drive.Empty() ? "empty" : drive.Size()); Events["ToggleUI"].active = !IsPrivate(); // show TakeData eva action button, if there is something to take Events["TakeData"].active = !drive.Empty(); // 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 = !IsPrivate() && v != null && v.isEVA && !EVA.IsDead(v); // hide TransferLocation button var transferVisible = !IsPrivate(); if (transferVisible) { transferVisible = Drive.GetDrives(vessel, true).Count > 1; } Events["TransferData"].active = transferVisible; Events["TransferData"].guiActive = transferVisible; } }
static bool Prefix(DeployedScienceExperiment __instance, ref bool __result) { // get private vars ScienceSubject subject = Lib.ReflectionValue <ScienceSubject>(__instance, "subject"); float storedScienceData = Lib.ReflectionValue <float>(__instance, "storedScienceData"); float transmittedScienceData = Lib.ReflectionValue <float>(__instance, "transmittedScienceData"); Vessel ControllerVessel = Lib.ReflectionValue <Vessel>(__instance, "ControllerVessel"); //Lib.Log("SendDataToComms!: " + subject.title); if (__instance.Experiment != null && !(__instance.ExperimentVessel == null) && subject != null && !(__instance.Cluster == null) && __instance.sciencePart.Enabled && !(storedScienceData <= 0f) && __instance.ExperimentSituationValid) { /* if (!__instance.TimeToSendStoredData()) * { * __result = true; * Lib.Log(Lib.BuildString("BREAKING GROUND bailout 1")); * return false; * } */ if (ControllerVessel == null && __instance.Cluster != null) { Lib.ReflectionCall(__instance, "SetControllerVessel"); ControllerVessel = Lib.ReflectionValue <Vessel>(__instance, "ControllerVessel"); } /* * Part control; * FlightGlobals.FindLoadedPart(__instance.Cluster.ControlModulePartId, out control); * if(control == null) { * //Lib.Log("DeployedScienceExperiment: couldn't find control module"); * __result = true; * Lib.Log(Lib.BuildString("BREAKING GROUND bailout 2")); * return false; * } */ List <Drive> drives = Drive.GetDrives(ControllerVessel, false); SubjectData subjectData = ScienceDB.GetSubjectDataFromStockId(subject.id); foreach (Drive drive in drives) { //Lib.Log(Lib.BuildString("BREAKING GROUND -- ", subject.id, " | ", storedScienceData.ToString())); if (drive.Record_file(subjectData, storedScienceData, true)) { //Lib.Log("BREAKING GROUND -- file recorded!"); Lib.ReflectionValue <float>(__instance, "transmittedScienceData", transmittedScienceData + storedScienceData); Lib.ReflectionValue <float>(__instance, "storedScienceData", 0f); break; } else { //Lib.Log("BREAKING GROUND -- file NOT recorded!"); __result = true; return(false); } } __result = false; } return(false); // always return false so we don't continue to the original code }
private static Drive FindDrive(Vessel v, string filename) { foreach (var d in Drive.GetDrives(v, true)) { if (d.files.ContainsKey(filename)) { return(d); } } return(null); }
// remove a file from a vessel public static void RemoveFile(Vessel v, string subject_id, double amount) { if (!Cache.VesselInfo(v).is_valid) { return; } foreach (var d in Drive.GetDrives(v, true)) { d.Delete_file(subject_id, amount, v.protoVessel); } }
// remove a sample from a vessel public static double RemoveSample(Vessel v, string subject_id, double amount) { if (!Cache.VesselInfo(v).is_valid) { return(0); } double massRemoved = 0; foreach (var d in Drive.GetDrives(v, true)) { massRemoved += d.Delete_sample(subject_id, amount); } return(massRemoved); }
// return name of file being transmitted from vessel specified public static string Transmitting(Vessel v, bool linked) { // never transmitting if science system is disabled if (!Features.Science) { return(string.Empty); } // not transmitting if unlinked if (!linked) { return(string.Empty); } // not transmitting if there is no ec left if (!Lib.IsPowered(v)) { return(string.Empty); } foreach (var p in Cache.WarpCache(v).files) { return(p.Key); } double now = Planetarium.GetUniversalTime(); double maxXmitValue = -1; string result = string.Empty; // get first file flagged for transmission, AND has a ts at least 5 seconds old or is > 0.001Mb in size foreach (var drive in Drive.GetDrives(v, true)) { foreach (var p in drive.files) { if (drive.GetFileSend(p.Key) && (p.Value.ts + 3 < now || p.Value.size > min_file_size)) { // prioritize whichever file has the most science points per byte var xmitValue = Value(p.Key, p.Value.size) / p.Value.size; if (string.IsNullOrEmpty(result) || xmitValue > maxXmitValue) { result = p.Key; maxXmitValue = xmitValue; } } } } return(result); }
// return size of a sample in a vessel drive public static double SampleSize(Vessel v, string subject_id) { if (!Cache.VesselInfo(v).is_valid) { return(0.0); } foreach (var d in Drive.GetDrives(v, true)) { if (d.samples.ContainsKey(subject_id)) { return(d.samples[subject_id].size); } } return(0.0); }
// get next sample to analyze, return null if there isn't a sample private static SubjectData NextSample(Vessel v) { foreach (var drive in Drive.GetDrives(v, true)) { // for each sample foreach (Sample sample in drive.samples.Values) { // if flagged for analysis if (sample.analyze) { return(sample.subjectData); } } } // there was no sample to analyze return(null); }
// get next sample to analyze, return null if there isn't a sample private static string NextSample(Vessel v) { foreach (var drive in Drive.GetDrives(v, true)) { // for each sample foreach (KeyValuePair <string, Sample> sample in drive.samples) { // if flagged for analysis if (sample.Value.analyze) { return(sample.Key); } } } // there was no sample to analyze return(null); }
// return name of file being transmitted from vessel specified public static string Transmitting(Vessel v, bool linked) { // never transmitting if science system is disabled if (!Features.Science) { return(string.Empty); } // not transmitting if unlinked if (!linked) { return(string.Empty); } // not transmitting if there is no ec left if (!Lib.IsPowered(v)) { return(string.Empty); } foreach (var p in Cache.WarpCache(v).files) { return(p.Key); } // get first file flagged for transmission, AND has a ts at least 5 seconds old or is > 0.001Mb in size foreach (var drive in Drive.GetDrives(v, true)) { double now = Planetarium.GetUniversalTime(); foreach (var p in drive.files) { if (drive.GetFileSend(p.Key) && (p.Value.ts + 3 < now || p.Value.size > min_file_size)) { return(p.Key); } } } // no file flagged for transmission return(string.Empty); }
internal static double RecordData(ScienceData data, MetaData meta) { double remaining = data.dataAmount; foreach (var drive in Drive.GetDrives(meta.vessel.KerbalismData(), false)) { var size = Math.Min(remaining, drive.FileCapacityAvailable()); if (size > 0) { drive.Record_file(meta.subjectData, size, true, true); remaining -= size; } } if (remaining > 0) { Message.Post( Lib.Color(Lib.BuildString(meta.subjectData.FullTitle, " stored partially"), Lib.Kolor.Orange), "Not enough space on hard drive" ); } return(remaining); }
// 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; 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); // 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); } if (xmitScienceValue > 0.0) { // add science to the subject (and eventually included subjects), trigger completion events, credit the science, return how much has been credited. vd.scienceTransmitted += xmitFile.file.subjectData.RetrieveScience(xmitScienceValue, true, v.protoVessel, xmitFile.file); } } UnityEngine.Profiling.Profiler.EndSample(); // 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 * elapsed_s)); ec.Consume(transmissionCost * elapsed_s, ResourceBroker.CommsXmit); }
void VesselRecoveryProcessing(ProtoVessel v, MissionRecoveryDialog dialog, float score) { // note: // this function accumulate science stored in drives on recovery, // and visualize the data in the recovery dialog window // do nothing if science system is disabled, or in sandbox mode if (!Features.Science || HighLogic.CurrentGame.Mode == Game.Modes.SANDBOX) { return; } var vesselID = Lib.VesselID(v); // get the drive data from DB if (!DB.vessels.ContainsKey(vesselID)) { return; } foreach (Drive drive in Drive.GetDrives(v)) { // for each file in the drive foreach (KeyValuePair <string, File> p in drive.files) { // shortcuts string filename = p.Key; File file = p.Value; // de-buffer partially transmitted data file.size += file.buff; file.buff = 0.0; // get subject ScienceSubject subject = ResearchAndDevelopment.GetSubjectByID(filename); // credit science float credits = Science.Credit(filename, file.size, false, v); // create science widged ScienceSubjectWidget widged = ScienceSubjectWidget.Create ( subject, // subject (float)file.size, // data gathered credits, // science points dialog // recovery dialog ); // add widget to dialog dialog.AddDataWidget(widged); // add science credits to total dialog.scienceEarned += (float)credits; } // for each sample in the drive // for each file in the drive foreach (KeyValuePair <string, Sample> p in drive.samples) { // shortcuts string filename = p.Key; Sample sample = p.Value; // get subject ScienceSubject subject = ResearchAndDevelopment.GetSubjectByID(filename); // credit science float credits = Science.Credit(filename, sample.size, false, v); // create science widged ScienceSubjectWidget widged = ScienceSubjectWidget.Create ( subject, // subject (float)sample.size, // data gathered credits, // science points dialog // recovery dialog ); // add widget to dialog dialog.AddDataWidget(widged); // add science credits to total dialog.scienceEarned += (float)credits; } } }
public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, KerbalismScansat kerbalismScansat, Part part_prefab, VesselData vd, Resource_info ec, double elapsed_s) { List <ProtoPartModuleSnapshot> scanners = Cache.VesselObjectsCache <List <ProtoPartModuleSnapshot> >(vessel, "scansat_" + p.flightID); if (scanners == null) { scanners = Lib.FindModules(p, "SCANsat"); if (scanners.Count == 0) { scanners = Lib.FindModules(p, "ModuleSCANresourceScanner"); } Cache.SetVesselObjectsCache(vessel, "scansat_" + p.flightID, scanners); } if (scanners.Count == 0) { return; } var scanner = scanners[0]; bool is_scanning = Lib.Proto.GetBool(scanner, "scanning"); if (is_scanning && kerbalismScansat.ec_rate > double.Epsilon) { ec.Consume(kerbalismScansat.ec_rate * elapsed_s, "scanner"); } if (!Features.Science) { if (is_scanning && ec.amount < double.Epsilon) { SCANsat.StopScanner(vessel, scanner, 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>", vessel.vesselName, "</b>")); } } else if (vd.scansat_id.Contains(p.flightID)) { // if there is enough ec // note: comparing against amount in previous simulation step // re-enable at 25% EC if (ec.level > 0.25) { // re-enable the scanner SCANsat.ResumeScanner(vessel, 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>", vessel.vesselName, "</b>")); } } } // forget active scanners if (is_scanning) { vd.scansat_id.Remove(p.flightID); } return; } // if(!Feature.Science) string body_name = Lib.Proto.GetString(m, "body_name"); int sensorType = (int)Lib.Proto.GetUInt(m, "sensorType"); double body_coverage = Lib.Proto.GetDouble(m, "body_coverage"); double warp_buffer = Lib.Proto.GetDouble(m, "warp_buffer"); double new_coverage = SCANsat.Coverage(sensorType, vessel.mainBody); if (body_name == vessel.mainBody.name && new_coverage < body_coverage) { // SCANsat sometimes reports a coverage of 0, which is wrong new_coverage = body_coverage; } if (vessel.mainBody.name != body_name) { body_name = vessel.mainBody.name; body_coverage = new_coverage; } else { double coverage_delta = new_coverage - body_coverage; body_coverage = new_coverage; if (is_scanning) { Science.Generate_subject(kerbalismScansat.experimentType, vessel); var subject_id = Science.Generate_subject_id(kerbalismScansat.experimentType, vessel); var exp = Science.Experiment(subject_id); double size = exp.max_amount * coverage_delta / 100.0; // coverage is 0-100% size += warp_buffer; if (size > double.Epsilon) { // store what we can foreach (var d in Drive.GetDrives(vessel)) { var available = d.FileCapacityAvailable(); var chunk = Math.Min(size, available); if (!d.Record_file(subject_id, chunk, true)) { break; } size -= chunk; if (size < double.Epsilon) { break; } } } if (size > double.Epsilon) { // we filled all drives up to the brim but were unable to store everything if (warp_buffer < double.Epsilon) { // warp buffer is empty, so lets store the rest there warp_buffer = size; size = 0; } else { // warp buffer not empty. that's ok if we didn't get new data if (coverage_delta < double.Epsilon) { size = 0; } // else we're scanning too fast. stop. } } // we filled all drives up to the brim but were unable to store everything // cancel scanning and annoy the user if (size > double.Epsilon || ec.amount < double.Epsilon) { warp_buffer = 0; SCANsat.StopScanner(vessel, scanner, part_prefab); vd.scansat_id.Add(p.flightID); if (vd.cfg_ec) { Message.Post(Lib.BuildString("SCANsat sensor was disabled on <b>", vessel.vesselName, "</b>")); } } } else if (vd.scansat_id.Contains(p.flightID)) { var vi = Cache.VesselInfo(vessel); if (ec.level >= 0.25 && (vi.free_capacity / vi.total_capacity > 0.9)) { SCANsat.ResumeScanner(vessel, scanner, part_prefab); vd.scansat_id.Remove(p.flightID); if (vd.cfg_ec) { Message.Post(Lib.BuildString("SCANsat sensor resumed operations on <b>", vessel.vesselName, "</b>")); } } } } Lib.Proto.Set(m, "warp_buffer", warp_buffer); Lib.Proto.Set(m, "body_coverage", body_coverage); Lib.Proto.Set(m, "body_name", body_name); }
private static void GetFilesToTransmit(Vessel v, VesselData vd) { UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Science.GetFilesToTransmit"); Drive warpCache = Cache.WarpCache(v); xmitFiles.Clear(); List <SubjectData> filesToRemove = new List <SubjectData>(); foreach (Drive drive in Drive.GetDrives(vd, true)) { foreach (File f in drive.files.Values) { // always reset transmit rate f.transmitRate = 0.0; // delete empty files that aren't being transmitted // note : this won't work in case the same subject is split over multiple files (on different drives) if (f.size <= 0.0 && (!warpCache.files.ContainsKey(f.subjectData) || warpCache.files[f.subjectData].size <= 0.0)) { filesToRemove.Add(f.subjectData); continue; } // get files tagged for transmit if (drive.GetFileSend(f.subjectData.Id)) { xmitFiles.Add(new XmitFile(f, drive, f.subjectData.SciencePerMB, false)); } } // delete found empty files from the drive foreach (SubjectData fileToRemove in filesToRemove) { drive.files.Remove(fileToRemove); } filesToRemove.Clear(); } // sort files by science value per MB ascending order so high value files are transmitted first // because XmitFile list is processed from end to start UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Science.GetFilesToTransmit-Sort"); xmitFiles.Sort((x, y) => x.sciencePerMB.CompareTo(y.sciencePerMB)); UnityEngine.Profiling.Profiler.EndSample(); // add all warpcache files to the beginning of the XmitFile list foreach (File f in warpCache.files.Values) { // don't transmit empty files if (f.size <= 0.0) { continue; } // find the file on a "real" drive that correspond to this warpcache file // this allow to use the real file for displaying transmit info and saving state (filemanager, monitor, vesseldata...) int driveFileIndex = xmitFiles.FindIndex(df => df.file.subjectData == f.subjectData); if (driveFileIndex >= 0) { xmitFiles.Add(new XmitFile(f, warpCache, f.subjectData.SciencePerMB, true, xmitFiles[driveFileIndex].file)); } else { xmitFiles.Add(new XmitFile(f, warpCache, f.subjectData.SciencePerMB, true)); // should not be happening, but better safe than sorry } } UnityEngine.Profiling.Profiler.EndSample(); }
public void Update() { if (drive == null) { return; } if (Lib.IsEditor()) { bool update = false; if (dataCapacities != null) { foreach (var c in dataCapacities) { if (c.Key == dataCapacityUI) { update |= effectiveDataCapacity != c.Value; effectiveDataCapacity = c.Value; } } } if (sampleCapacities != null) { foreach (var c in sampleCapacities) { if (c.Key == sampleCapacityUI) { update |= effectiveSampleCapacity != c.Value; effectiveSampleCapacity = c.Value; } } } drive.dataCapacity = effectiveDataCapacity; drive.sampleCapacity = effectiveSampleCapacity; Fields["sampleCapacityUI"].guiActiveEditor = sampleCapacity > 0; Fields["dataCapacityUI"].guiActiveEditor = dataCapacity > 0; if (update) { GameEvents.onEditorShipModified.Fire(EditorLogic.fetch.ship); UpdateCapacity(); } } if (Lib.IsFlight()) { // show DATA UI button, with size info Events["ToggleUI"].guiName = Lib.StatusToggle("Data", drive.Empty() ? "empty" : drive.Size()); Events["ToggleUI"].active = !IsPrivate(); // show TakeData eva action button, if there is something to take Events["TakeData"].active = !drive.Empty(); // 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 = !IsPrivate() && v != null && v.isEVA && !EVA.IsDead(v); // hide TransferLocation button var transferVisible = !IsPrivate(); if (transferVisible) { transferVisible = Drive.GetDrives(vessel, true).Count > 1; } Events["TransferData"].active = transferVisible; Events["TransferData"].guiActive = transferVisible; } }
// analyze a sample private static Status Analyze(Vessel v, SubjectData subject, double amount) { Sample sample = null; Drive sampleDrive = null; foreach (var d in Drive.GetDrives(v, true)) { if (d.samples.ContainsKey(subject) && d.samples[subject].analyze) { sample = d.samples[subject]; sampleDrive = d; break; } } bool completed = false; if (sample != null) { completed = amount > sample.size; amount = Math.Min(amount, sample.size); } Drive fileDrive = Drive.FileDrive(v.KerbalismData(), amount); if (fileDrive == null) { return(Status.NO_STORAGE); } if (sample != null) { bool recorded = fileDrive.Record_file(subject, amount, false); double massRemoved = 0.0; if (recorded) { massRemoved = sampleDrive.Delete_sample(subject, amount); } else { Message.Post( Lib.Color(Lib.BuildString(Localizer.Format("#KERBALISM_Laboratory_Analysis"), " stopped"), Lib.Kolor.Red), "Not enough space on hard drive" ); return(Status.NO_STORAGE); } // return sample mass to experiment if needed if (massRemoved > 0.0) { RestoreSampleMass(v, subject, massRemoved); } } // if the analysis is completed if (completed) { if (!PreferencesScience.Instance.analyzeSamples) { // only inform the user if auto-analyze is turned off // otherwise we could be spamming "Analysis complete" messages Message.Post(Lib.BuildString(Lib.Color(Localizer.Format("#KERBALISM_Laboratory_Analysis"), Lib.Kolor.Science, true), "\n", Localizer.Format("#KERBALISM_Laboratory_Analyzed", Lib.Bold(v.vesselName), Lib.Bold(subject.FullTitle))), localized_results); } if (PreferencesScience.Instance.transmitScience) { fileDrive.Send(subject.Id, true); } // record landmark event if (!Lib.Landed(v)) { DB.landmarks.space_analysis = true; } } return(Status.RUNNING); }
void Record(MetaData meta, ScienceData data, bool send) { // if amount is zero, warn the user and do nothing else if (data.dataAmount <= double.Epsilon) { Message.Post("There is no more useful data here"); return; } // if this is a sample and we are trying to send it, warn the user and do nothing else if (meta.is_sample && send) { Message.Post("We can't transmit a sample", "It needs to be recovered, or analyzed in a lab"); return; } // record data in the drive bool recorded = false; if (!meta.is_sample) { Drive drive = Drive.FileDrive(meta.vessel, data.dataAmount); recorded = drive.Record_file(data.subjectID, data.dataAmount); } else { Drive drive = Drive.SampleDrive(meta.vessel, data.dataAmount, data.subjectID); var experimentInfo = Science.Experiment(data.subjectID); var sampleMass = Science.GetSampleMass(data.subjectID); var mass = sampleMass / experimentInfo.max_amount * data.dataAmount; recorded = drive.Record_sample(data.subjectID, data.dataAmount, mass); } if (recorded) { // flag for sending if specified if (!meta.is_sample && send) { foreach (var d in Drive.GetDrives(meta.vessel)) { d.Send(data.subjectID, true); } } // render experiment inoperable if necessary if (!meta.is_rerunnable) { meta.experiment.SetInoperable(); } // dismiss the dialog and popups Dismiss(data); var exp = Science.Experiment(data.subjectID); // inform the user Message.Post( Lib.BuildString("<b>", exp.FullName(data.subjectID), "</b> recorded"), !meta.is_rerunnable ? Localizer.Format("#KERBALISM_Science_inoperable") : string.Empty ); } else { var exp = Science.Experiment(data.subjectID); Message.Post( Lib.Color("red", Lib.BuildString(exp.FullName(data.subjectID), " can not be stored")), "Not enough space on hard drive" ); } }
public void FixedUpdate() { if (scanner == null) { return; } if (!Features.Science) { return; } IsScanning = SCANsat.IsScanning(scanner); double new_coverage = SCANsat.Coverage(sensorType, vessel.mainBody); if (body_name == vessel.mainBody.name && new_coverage < body_coverage) { // SCANsat sometimes reports a coverage of 0, which is wrong new_coverage = body_coverage; } if (vessel.mainBody.name != body_name) { body_name = vessel.mainBody.name; body_coverage = new_coverage; } else { double coverage_delta = new_coverage - body_coverage; body_coverage = new_coverage; var vd = DB.Vessel(vessel); if (IsScanning) { Science.Generate_subject(experimentType, vessel); var subject_id = Science.Generate_subject_id(experimentType, vessel); var exp = Science.Experiment(subject_id); double size = exp.max_amount * coverage_delta / 100.0; // coverage is 0-100% size += warp_buffer; if (size > double.Epsilon) { // store what we can foreach (var d in Drive.GetDrives(vessel)) { var available = d.FileCapacityAvailable(); var chunk = Math.Min(size, available); if (!d.Record_file(subject_id, chunk, true)) { break; } size -= chunk; if (size < double.Epsilon) { break; } } } if (size > double.Epsilon) { // we filled all drives up to the brim but were unable to store everything if (warp_buffer < double.Epsilon) { // warp buffer is empty, so lets store the rest there warp_buffer = size; size = 0; } else { // warp buffer not empty. that's ok if we didn't get new data if (coverage_delta < double.Epsilon) { size = 0; } // else we're scanning too fast. stop. } // cancel scanning and annoy the user if (size > double.Epsilon) { warp_buffer = 0; StopScan(); vd.scansat_id.Add(part.flightID); Message.Post(Lib.Color("red", "Scanner halted", true), "Scanner halted on <b>" + vessel.vesselName + "</b>. No storage left on vessel."); } } } else if (vd.scansat_id.Contains(part.flightID)) { var vi = Cache.VesselInfo(vessel); if (vi.free_capacity / vi.total_capacity > 0.9) // restart when 90% of capacity is available { StartScan(); vd.scansat_id.Remove(part.flightID); if (vd.cfg_ec) { Message.Post(Lib.BuildString("SCANsat sensor resumed operations on <b>", vessel.vesselName, "</b>")); } } } } }
static bool Prefix(ref ProtoVessel pv, ref bool quick) { if (pv == null) { return(true); } // get a hard drive. any hard drive will do, no need to find a specific one. ProtoPartModuleSnapshot protoHardDrive = null; foreach (var p in pv.protoPartSnapshots) { foreach (var pm in Lib.FindModules(p, "HardDrive")) { protoHardDrive = pm; break; } if (protoHardDrive != null) { break; } } if (protoHardDrive == null) { return(true); // no drive on the vessel - nothing to do. } double scienceToCredit = 0.0; List <DialogGUIBase> labels = new List <DialogGUIBase>(); foreach (Drive drive in Drive.GetDrives(pv, true)) { foreach (File file in drive.files.Values) { double subjectValue = file.subjectData.ScienceValue(file.size); file.subjectData.RemoveScienceCollectedInFlight(subjectValue); if (file.useStockCrediting) { file.ConvertToStockData().Save(protoHardDrive.moduleValues.AddNode("ScienceData")); if (!file.subjectData.ExistsInRnD) { file.subjectData.CreateSubjectInRnD(); } file.subjectData.SetAsPersistent(); file.subjectData.UpdateSubjectCompletion(subjectValue); } else { scienceToCredit += file.subjectData.RetrieveScience(subjectValue, false, pv); labels.Add(new DialogGUILabel(Lib.BuildString( Lib.Color("+ " + subjectValue.ToString("F1"), Lib.Kolor.Science), " (", Lib.Color(file.subjectData.ScienceRetrievedInKSC.ToString("F1"), Lib.Kolor.Science, true), " / ", Lib.Color(file.subjectData.ScienceMaxValue.ToString("F1"), Lib.Kolor.Science, true), ") : ", file.subjectData.FullTitle ))); } } foreach (Sample sample in drive.samples.Values) { double subjectValue = sample.subjectData.ScienceValue(sample.size); sample.subjectData.RemoveScienceCollectedInFlight(subjectValue); if (sample.useStockCrediting) { sample.ConvertToStockData().Save(protoHardDrive.moduleValues.AddNode("ScienceData")); if (!sample.subjectData.ExistsInRnD) { sample.subjectData.CreateSubjectInRnD(); } sample.subjectData.SetAsPersistent(); sample.subjectData.UpdateSubjectCompletion(subjectValue); } else { scienceToCredit += sample.subjectData.RetrieveScience(subjectValue, false, pv); labels.Add(new DialogGUILabel(Lib.BuildString( Lib.Color("+ " + subjectValue.ToString("F1"), Lib.Kolor.Science), " (", Lib.Color(sample.subjectData.ScienceRetrievedInKSC.ToString("F1"), Lib.Kolor.Science, true), " / ", Lib.Color(sample.subjectData.ScienceMaxValue.ToString("F1"), Lib.Kolor.Science, true), ") : ", sample.subjectData.FullTitle ))); } } } protoHardDrive.moduleName = "ModuleScienceContainer"; if (scienceToCredit > 0.0) { // ideally we should hack the stock dialog to add the little science widgets to it but I'm lazy // plus it looks like crap anyway PopupDialog.SpawnPopupDialog ( new MultiOptionDialog ( "scienceResults", "", pv.vesselName + " " + Local.VesselRecovery_title, HighLogic.UISkin, new Rect(0.3f, 0.5f, 350f, 100f), //" recovery" new DialogGUIVerticalLayout ( new DialogGUIBox(Local.VesselRecovery_info + " : " + Lib.Color(scienceToCredit.ToString("F1") + " " + Local.VesselRecovery_CREDITS, Lib.Kolor.Science, true), 340f, 30f), //"SCIENCE RECOVERED"" CREDITS" new DialogGUIScrollList ( new Vector2(340f, 250f), false, true, new DialogGUIVerticalLayout(labels.ToArray()) ), new DialogGUIButton(Local.VesselRecovery_OKbutton, null, 340f, 30f, true, HighLogic.UISkin.button) //"OK" ) ), false, HighLogic.UISkin ); } return(true); // continue to the original code }
// 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(); }
void Record(MetaData meta, ScienceData data, bool send) { // if amount is zero, warn the user and do nothing else if (data.dataAmount <= double.Epsilon) { Message.Post("There is no more useful data here"); return; } if (meta.subjectData == null) { return; } // if this is a sample and we are trying to send it, warn the user and do nothing else if (meta.is_sample && send) { Message.Post("We can't transmit a sample", "It needs to be recovered, or analyzed in a lab"); return; } // record data in the drive bool recorded = false; bool partial_record = false; if (!meta.is_sample) { var remaining = MiniHijacker.RecordData(data, meta); if (remaining > 0) { partial_record = true; } recorded = remaining < data.dataAmount; } else { Drive drive = Drive.SampleDrive(meta.vessel.KerbalismData(), data.dataAmount, meta.subjectData); if (drive != null) { recorded = drive.Record_sample(meta.subjectData, data.dataAmount, meta.subjectData.ExpInfo.MassPerMB * data.dataAmount, true); } } if (recorded) { // flag for sending if specified if (!meta.is_sample && send) { foreach (var d in Drive.GetDrives(meta.vessel)) { d.Send(data.subjectID, true); } } // render experiment inoperable if necessary if (!meta.is_rerunnable && !partial_record) { meta.experiment.SetInoperable(); } // dismiss the dialog and popups Dismiss(data); if (!partial_record) { // inform the user Message.Post( Lib.BuildString("<b>", meta.subjectData.FullTitle, "</b> recorded"), !meta.is_rerunnable ? Local.Science_inoperable : string.Empty ); } } else { Message.Post( Lib.Color(Lib.BuildString(meta.subjectData.FullTitle, " can not be stored"), Lib.Kolor.Red), "Not enough space on hard drive" ); } }