public virtual void FixedUpdate() { if (!Lib.IsFlight() || module == null) { return; } if (isBroken) { if (isBroken != lastFixedBrokenState) { lastFixedBrokenState = isBroken; FixModule(!isBroken); } } else if (hasFixedEnergyChanged != hasEnergy) { hasFixedEnergyChanged = hasEnergy; lastFixedBrokenState = false; // Update module FixModule(hasEnergy); } // If isConsuming if (isConsuming && resources != null) { resources.Consume(actualCost * Kerbalism.elapsed_s); } }
public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Deploy deploy, Resource_info ec, double elapsed_s) { if (deploy.isConsuming) { ec.Consume(deploy.extra_Cost * elapsed_s); } }
public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Laboratory lab, Resource_info ec, double elapsed_s) { // if enabled if (Lib.Proto.GetBool(m, "running")) { // if a researcher is not required, or the researcher is present background_researcher_cs = new CrewSpecs(lab.researcher); if (!background_researcher_cs || background_researcher_cs.Check(p.protoModuleCrew)) { // get sample to analyze background_sample = NextSample(v); // if there is a sample to analyze if (background_sample != null) { // consume EC ec.Consume(lab.ec_rate * elapsed_s); // if there was ec // - comparing against amount in previous simulation step if (ec.amount > double.Epsilon) { // analyze the sample Analyze(v, background_sample, lab.analysis_rate * elapsed_s); } } } } }
static void ProcessLight(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleLight light, Resource_info ec, double elapsed_s) { if (light.useResources && Lib.Proto.GetBool(m, "isOn")) { ec.Consume(light.resourceAmount * elapsed_s); } }
public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, GravityRing ring, Resource_info ec, double elapsed_s) { // if the module is either non-deployable or deployed if (ring.deploy.Length == 0 || Lib.Proto.GetBool(m, "deployed")) { // consume ec ec.Consume(ring.ec_rate * elapsed_s); } }
public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Emitter emitter, Resource_info ec, double elapsed_s) { // if enabled, and EC is required if (Lib.Proto.GetBool(m, "running") && emitter.ec_rate > double.Epsilon) { // consume EC ec.Consume(emitter.ec_rate * elapsed_s); } }
public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) { return; } // if enabled if (running) { // if a researcher is not required, or the researcher is present if (!researcher_cs || researcher_cs.Check(part.protoModuleCrew)) { // get next sample to analyze current_sample = NextSample(vessel); // if there is a sample to analyze if (current_sample != null) { // consume EC ec = ResourceCache.Info(vessel, "ElectricCharge"); ec.Consume(ec_rate * Kerbalism.elapsed_s); // if there was ec // - comparing against amount in previous simulation step if (ec.amount > double.Epsilon) { // analyze the sample Analyze(vessel, current_sample, analysis_rate * Kerbalism.elapsed_s); status = Status.RUNNING; } // if there was no ec else { status = Status.NO_EC; } } // if there is no sample to analyze else { status = Status.NO_SAMPLE; } } // if a researcher is required, but missing else { status = Status.NO_RESEARCHER; } } // if disabled else { status = Status.DISABLED; } }
static void ProcessStockLab(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleScienceConverter lab, Resource_info ec, double elapsed_s) { // note: we are only simulating the EC consumption // note: there is no easy way to 'stop' the lab when there isn't enough EC // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // consume ec ec.Consume(lab.powerRequirement * elapsed_s); } }
public virtual void FixedUpdate() { if (Lib.IsFlight() && Features.Deploy) { if (isConsuming) { if (resourceInfo != null) { resourceInfo.Consume(actualECCost * Kerbalism.elapsed_s); } } } }
public static void Update(Vessel v) { // do nothing if not an eva kerbal if (!v.isEVA) return; // get KerbalEVA module KerbalEVA kerbal = Lib.FindModules<KerbalEVA>(v)[0]; Vessel_info vi = Cache.VesselInfo(v); // Stock KSP adds 5 units of monoprop to EVAs. We want to limit that amount // to whatever was available in the ship, so we don't magically create EVA prop out of nowhere if(Cache.HasVesselObjectsCache(v, "eva_prop")) { Lib.Log("### have eva_prop for " + v); var quantity = Cache.VesselObjectsCache<double>(v, "eva_prop"); Cache.RemoveVesselObjectsCache(v, "eva_prop"); Lib.Log("### adding " + quantity + " eva prop"); Lib.SetResource(kerbal.part, Lib.EvaPropellantName(), quantity, Lib.EvaPropellantCapacity()); } // get resource handler Resource_info ec = ResourceCache.Info(v, "ElectricCharge"); // determine if headlamps need ec // - not required if there is no EC capacity in eva kerbal (no ec supply in profile) // - not required if no EC cost for headlamps is specified (set by the user) bool need_ec = ec.capacity > double.Epsilon && Settings.HeadLampsCost > double.Epsilon; // consume EC for the headlamps if (need_ec && kerbal.lampOn) { ec.Consume(Settings.HeadLampsCost * Kerbalism.elapsed_s, "headlamp"); } // force the headlamps on/off HeadLamps(kerbal, kerbal.lampOn && (!need_ec || ec.amount > double.Epsilon)); // if dead if (IsDead(v)) { // enforce freezed state Freeze(kerbal); // disable modules DisableModules(kerbal); // remove plant flag action kerbal.flagItems = 0; } }
public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) return; // if has any animation playing, consume energy. if (Is_consuming_energy()) { // get resource handler Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // consume ec ec.Consume(ec_rate * Kerbalism.elapsed_s); } }
private static void RunProcessTick(Vessel v, double elapsed_s, double ec_produced, List <KeyValuePair <string, double> > resourcesProduced, double ec_consumed, List <KeyValuePair <string, double> > resourcesConsumed, Resource_info ec, Vessel_resources resources) { // evaluate process rate double rate = 1; if (ec_consumed < ec.amount) { rate = ec.amount / ec_consumed; } foreach (var consumed in resourcesConsumed) { var ri = resources.Info(v, consumed.Key); rate = Math.Min(rate, Lib.Clamp(ri.amount / (consumed.Value * elapsed_s), 0, 1)); } foreach (var produced in resourcesProduced) { var ri = resources.Info(v, produced.Key); var capacityAvailable = ri.capacity - ri.amount; var amountProduced = produced.Value * elapsed_s; if (capacityAvailable < amountProduced) { rate = Math.Min(rate, Lib.Clamp(capacityAvailable / amountProduced, 0, 1)); } } // produce/consume according to rate if (rate < double.Epsilon) { return; } ec.Consume(ec_consumed * elapsed_s * rate, "module process"); ec.Produce(ec_produced * elapsed_s * rate, "module process"); foreach (var consumed in resourcesConsumed) { resources.Info(v, consumed.Key).Consume(consumed.Value * elapsed_s * rate, "module process"); } foreach (var produced in resourcesProduced) { resources.Info(v, produced.Key).Produce(produced.Value * elapsed_s * rate, "module process"); } }
public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Experiment exp, Resource_info ec, double elapsed_s) { // if experiment is active if (Lib.Proto.GetBool(m, "recording")) { // detect conditions // - comparing against amount in previous step bool has_ec = ec.amount > double.Epsilon; bool has_operator = new CrewSpecs(exp.crew).Check(v); string sit = Science.Situation(v, exp.situations); // deduce issues string 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>"; } Lib.Proto.Set(m, "issue", issue); // if there are no issues if (issue.Length == 0) { // generate subject id string subject_id = Science.Generate_subject(exp.experiment, v.mainBody, sit, Science.Biome(v, sit), Science.Multiplier(v, sit)); // record in drive if (exp.transmissible) { DB.Vessel(v).drive.Record_file(subject_id, exp.data_rate * elapsed_s); } else { DB.Vessel(v).drive.Record_sample(subject_id, exp.data_rate * elapsed_s); } // consume ec ec.Consume(exp.ec_rate * elapsed_s); } } }
public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) { return; } // if enabled, and there is ec consumption if (running && ec_rate > double.Epsilon) { // get resource cache Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // consume EC ec.Consume(ec_rate * Kerbalism.elapsed_s); } }
public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Laboratory lab, Resource_info ec, double elapsed_s) { // if enabled if (Lib.Proto.GetBool(m, "running")) { // if a researcher is not required, or the researcher is present background_researcher_cs = new CrewSpecs(lab.researcher); if (!background_researcher_cs || background_researcher_cs.Check(p.protoModuleCrew)) { double rate = lab.analysis_rate; if (background_researcher_cs) { int bonus = background_researcher_cs.Bonus(p.protoModuleCrew); double crew_gain = 1 + bonus * Settings.LaboratoryCrewLevelBonus; crew_gain = Lib.Clamp(crew_gain, 1, Settings.MaxLaborartoryBonus); rate *= crew_gain; } // get sample to analyze background_sample = NextSample(v); // if there is a sample to analyze if (background_sample != null) { // consume EC ec.Consume(lab.ec_rate * elapsed_s); // if there was ec // - comparing against amount in previous simulation step if (ec.amount > double.Epsilon) { // analyze the sample var status = Analyze(v, background_sample, rate * elapsed_s); if (status != Status.RUNNING) { Lib.Proto.Set(m, "running", false); } } } } } }
public static void Update(Vessel v) { // do nothing if not an eva kerbal if (!v.isEVA) return; // get KerbalEVA module KerbalEVA kerbal = Lib.FindModules<KerbalEVA>(v)[0]; // get resource handler Resource_info ec = ResourceCache.Info(v, "ElectricCharge"); // determine if headlamps need ec // - not required if there is no EC capacity in eva kerbal (no ec supply in profile) // - not required if no EC cost for headlamps is specified (set by the user) bool need_ec = ec.capacity > double.Epsilon && Settings.HeadLampsCost > double.Epsilon; // consume EC for the headlamps if (need_ec && kerbal.lampOn) { ec.Consume(Settings.HeadLampsCost * Kerbalism.elapsed_s); } // force the headlamps on/off HeadLamps(kerbal, kerbal.lampOn && (!need_ec || ec.amount > double.Epsilon)); // if dead if (IsDead(v)) { // enforce freezed state Freeze(kerbal); // disable modules DisableModules(kerbal); // remove plant flag action kerbal.flagItems = 0; } }
static void ProcessCryoTank(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule cryotank, Vessel_resources resources, Resource_info ec, double elapsed_s) { // Note. Currently background simulation of Cryotanks has an irregularity in that boiloff of a fuel type in a tank removes resources from all tanks // but at least some simulation is better than none ;) // get list of fuels, do nothing if no fuels IList fuels = Lib.ReflectionValue <IList>(cryotank, "fuels"); if (fuels == null) { return; } // is cooling available, note: comparing against amount in previous simulation step bool available = (Lib.Proto.GetBool(m, "CoolingEnabled") && ec.amount > double.Epsilon); // get cooling cost double cooling_cost = Lib.ReflectionValue <float>(cryotank, "CoolingCost"); string fuel_name = ""; double amount = 0.0; double total_cost = 0.0; double boiloff_rate = 0.0; foreach (var item in fuels) { fuel_name = Lib.ReflectionValue <string>(item, "fuelName"); // if fuel_name is null, don't do anything if (fuel_name == null) { continue; } //get fuel resource Resource_info fuel = resources.Info(v, fuel_name); // if there is some fuel // note: comparing against amount in previous simulation step if (fuel.amount > double.Epsilon) { // Try to find resource "fuel_name" in PartResources ProtoPartResourceSnapshot proto_fuel = p.resources.Find(k => k.resourceName == fuel_name); // If part doesn't have the fuel, don't do anything. if (proto_fuel == null) { continue; } // get amount in the part amount = proto_fuel.amount; // if cooling is enabled and there is enough EC if (available) { // calculate ec consumption total_cost += cooling_cost * amount * 0.001; } // if cooling is disabled or there wasn't any EC else { // get boiloff rate per-second boiloff_rate = Lib.ReflectionValue <float>(item, "boiloffRate") / 360000.0f; // let it boil off fuel.Consume(amount * (1.0 - Math.Pow(1.0 - boiloff_rate, elapsed_s))); } } } // apply EC consumption ec.Consume(total_cost * elapsed_s); }
private static bool DoRecord(Experiment experiment, string subject_id, Vessel vessel, Resource_info ec, uint hdId, Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs, double remainingSampleMass, double dataSampled, out double sampledOut, out double remainingSampleMassOut) { // default output values for early returns sampledOut = dataSampled; remainingSampleMassOut = remainingSampleMass; var exp = Science.Experiment(subject_id); if (Done(exp, dataSampled)) { return(true); } double elapsed = Kerbalism.elapsed_s; double chunkSize = Math.Min(experiment.data_rate * elapsed, exp.max_amount); double massDelta = experiment.sample_mass * chunkSize / exp.max_amount; Drive drive = GetDrive(experiment, vessel, hdId, chunkSize, subject_id); // on high time warp this chunk size could be too big, but we could store a sizable amount if we process less bool isFile = experiment.sample_mass < float.Epsilon; double maxCapacity = isFile ? drive.FileCapacityAvailable() : drive.SampleCapacityAvailable(subject_id); Drive warpCacheDrive = null; if (isFile) { if (drive.GetFileSend(subject_id)) { warpCacheDrive = Cache.WarpCache(vessel); } if (warpCacheDrive != null) { maxCapacity += warpCacheDrive.FileCapacityAvailable(); } } double factor = Rate(vessel, chunkSize, maxCapacity, elapsed, ec, experiment.ec_rate, resources, resourceDefs); if (factor < double.Epsilon) { return(false); } chunkSize *= factor; massDelta *= factor; elapsed *= factor; bool stored = false; if (chunkSize > double.Epsilon) { if (isFile) { if (warpCacheDrive != null) { double s = Math.Min(chunkSize, warpCacheDrive.FileCapacityAvailable()); stored = warpCacheDrive.Record_file(subject_id, s, true); if (chunkSize > s) // only write to persisted drive if the data cannot be transmitted in this tick { stored &= drive.Record_file(subject_id, chunkSize - s, true); } } else { stored = drive.Record_file(subject_id, chunkSize, true); } } else { stored = drive.Record_sample(subject_id, chunkSize, massDelta); } } if (!stored) { return(false); } // consume resources ec.Consume(experiment.ec_rate * elapsed, "experiment"); foreach (var p in resourceDefs) { resources.Consume(vessel, p.Key, p.Value * elapsed, "experiment"); } dataSampled += chunkSize; dataSampled = Math.Min(dataSampled, exp.max_amount); sampledOut = dataSampled; if (!experiment.sample_collecting) { remainingSampleMass -= massDelta; remainingSampleMass = Math.Max(remainingSampleMass, 0); } remainingSampleMassOut = remainingSampleMass; return(true); }
public void Execute(Vessel v, Vessel_info vi, Vessel_resources resources, double elapsed_s) { // store list of crew to kill List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>(); // get input resource handler Resource_info res = input.Length > 0 ? resources.Info(v, input) : null; // determine message variant uint variant = vi.temperature < Settings.SurvivalTemperature ? 0 : 1u; // get product of all environment modifiers double k = Modifiers.Evaluate(v, vi, resources, modifiers); // 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); // if continuous double step; if (interval <= double.Epsilon) { // influence consumption by elapsed time step = elapsed_s; } // if interval-based else { // accumulate time rd.time_since += elapsed_s; // determine number of steps step = Math.Floor(rd.time_since / interval); // consume time rd.time_since -= step * interval; // remember if a meal is consumed/produced in this simulation step res.meal_happened |= step > 0.99; if (output.Length > 0) { ResourceCache.Info(v, output).meal_happened |= step > 0.99; } } // if continuous, or if one or more intervals elapsed if (step > double.Epsilon) { // if there is a resource specified if (res != null && rate > double.Epsilon) { // determine amount of resource to consume double required = rate // consumption rate * k // product of environment modifiers * step; // seconds elapsed or number of steps // if there is no output if (output.Length == 0) { // simply consume (that is faster) res.Consume(required); } // if there is an output and monitor is false else if (!monitor) { // transform input into output resource // - rules always dump excess overboard (because it is waste) Resource_recipe recipe = new Resource_recipe(); recipe.Input(input, required); recipe.Output(output, required * ratio, true); resources.Transform(recipe); } // if monitor then do not consume input resource and only produce output if resource percentage + monitor_offset is < 100% else if ((res.amount / res.capacity) + monitor_offset < 1.0) { // simply produce (that is faster) resources.Produce(v, output, required * ratio); } } // degenerate: // - if the environment modifier is not telling to reset (by being zero) // - if the input threshold is reached if used // - if this rule is resource-less, or if there was not enough resource in the vessel if (input_threshold >= double.Epsilon) { if (res.amount >= double.Epsilon && res.capacity >= double.Epsilon) { trigger = (res.amount / res.capacity) + monitor_offset >= input_threshold; } else { trigger = false; } } else { trigger = input.Length == 0 || res.amount <= double.Epsilon; } if (k > 0.0 && trigger) { rd.problem += degeneration // degeneration rate per-second or per-interval * k // product of environment modifiers * step // seconds elapsed or by number of steps * Variance(c, variance); // kerbal-specific variance } // else slowly recover else { rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002); rd.problem = Math.Max(rd.problem, 0.0); } } // kill kerbal if necessary if (rd.problem >= fatal_threshold) { if (fatal_message.Length > 0) { Message.Post(breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(fatal_message, v, c, variant)); } if (breakdown) { // trigger breakdown event Misc.Breakdown(v, c); // 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; } } // execute the deferred kills foreach (ProtoCrewMember c in deferred_kills) { Misc.Kill(v, c); } }
public void Execute(Vessel v, Vessel_info vi, Vessel_resources resources, double elapsed_s) { // store list of crew to kill List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>(); // get input resource handler Resource_info res = input.Length > 0 ? resources.Info(v, input) : null; // determine message variant uint variant = vi.temperature < PreferencesLifeSupport.Instance.survivalTemperature ? 0 : 1u; // get product of all environment modifiers double k = Modifiers.Evaluate(v, vi, resources, modifiers); bool lifetime_enabled = PreferencesBasic.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; // if continuous double step; if (interval <= double.Epsilon) { // influence consumption by elapsed time step = elapsed_s; } // if interval-based else { // accumulate time rd.time_since += elapsed_s; // determine number of steps step = Math.Floor(rd.time_since / interval); // consume time rd.time_since -= step * interval; // remember if a meal is consumed/produced in this simulation step if (step > 0.99) { res.SetMealHappened(); } if (output.Length > 0 && step > 0.99) { ResourceCache.Info(v, output).SetMealHappened(); } } // if continuous, or if one or more intervals elapsed if (step > double.Epsilon) { double r = rate * Variance(name, c, individuality); // kerbal-specific variance // if there is a resource specified if (res != null && r > double.Epsilon) { // determine amount of resource to consume double required = r // consumption rate * k // product of environment modifiers * step; // seconds elapsed or number of steps // if there is no output if (output.Length == 0) { // simply consume (that is faster) res.Consume(required); } // if there is an output and monitor is false else if (!monitor) { // transform input into output resource // - rules always dump excess overboard (because it is waste) Resource_recipe recipe = new Resource_recipe((Part)null); // kerbals are not associated with a part recipe.Input(input, required); recipe.Output(output, required * ratio, true); resources.Transform(recipe); } // if monitor then do not consume input resource and only produce output if resource percentage + monitor_offset is < 100% else if ((res.amount / res.capacity) + monitor_offset < 1.0) { // simply produce (that is faster) resources.Produce(v, output, required * ratio); } } // degenerate: // - if the environment modifier is not telling to reset (by being zero) // - if the input threshold is reached if used // - if this rule is resource-less, or if there was not enough resource in the vessel if (input_threshold >= double.Epsilon) { if (res.amount >= double.Epsilon && res.capacity >= double.Epsilon) { trigger = (res.amount / res.capacity) + monitor_offset >= input_threshold; } else { trigger = false; } } else { trigger = input.Length == 0 || res.amount <= double.Epsilon; } if (k > 0.0 && trigger) { 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 && PreferencesBasic.Instance.stressBreakdowns) { // 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 *= PreferencesBasic.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 (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); } }
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); }
public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Experiment experiment, Resource_info ec, double elapsed_s) { var exp = ResearchAndDevelopment.GetExperiment(experiment.experiment_id); bool didPrepare = Lib.Proto.GetBool(m, "didPrepare", false); bool shrouded = Lib.Proto.GetBool(m, "shrouded", false); string last_subject_id = Lib.Proto.GetString(m, "last_subject_id", ""); double remainingSampleMass = Lib.Proto.GetDouble(m, "remainingSampleMass", 0); bool broken = Lib.Proto.GetBool(m, "broken", false); string subject_id; string issue = TestForIssues(v, exp, ec, experiment, broken, remainingSampleMass, didPrepare, shrouded, last_subject_id, out subject_id); Lib.Proto.Set(m, "issue", issue); double dataSampled = Lib.Proto.GetDouble(m, "dataSampled"); if (last_subject_id != subject_id) { dataSampled = 0; } // if experiment is active if (!Lib.Proto.GetBool(m, "recording")) { return; } // if there are no issues if (issue.Length == 0 && dataSampled < exp.scienceCap * exp.dataScale) { Lib.Proto.Set(m, "last_subject_id", subject_id); // record in drive double chunkSize = experiment.data_rate * elapsed_s; var info = Science.Experiment(subject_id); double massDelta = experiment.sample_mass * chunkSize / info.max_amount; bool isSample = experiment.sample_mass < float.Epsilon; var drive = isSample ? DB.Vessel(v).BestDrive(chunkSize, 0) : DB.Vessel(v).BestDrive(0, Lib.SampleSizeToSlots(chunkSize)); // on high time warp this chunk size could be too big, but we could store a sizable amount if we processed less double maxCapacity = isSample ? drive.FileCapacityAvailable() : drive.SampleCapacityAvailable(subject_id); if (maxCapacity < chunkSize) { double factor = maxCapacity / chunkSize; chunkSize *= factor; massDelta *= factor; elapsed_s *= factor; } bool stored = false; if (experiment.sample_mass < float.Epsilon) { stored = drive.Record_file(subject_id, chunkSize, true, true); } else { stored = drive.Record_sample(subject_id, chunkSize, massDelta); } if (stored) { // consume ec ec.Consume(experiment.ec_rate * elapsed_s); dataSampled += chunkSize; dataSampled = Math.Min(dataSampled, exp.scienceCap * exp.dataScale); if (!experiment.sample_collecting) { remainingSampleMass -= massDelta; remainingSampleMass = Math.Max(remainingSampleMass, 0); Lib.Proto.Set(m, "remainingSampleMass", remainingSampleMass); } Lib.Proto.Set(m, "dataSampled", dataSampled); } else { issue = "insufficient storage"; Lib.Proto.Set(m, "issue", issue); } } }
// trigger a random breakdown event public static void Breakdown(Vessel v, ProtoCrewMember c) { // constants const double res_penalty = 0.1; // proportion of food lost on 'depressed' and 'wrong_valve' // get a supply resource at random Resource_info res = null; if (Profile.supplies.Count > 0) { Supply supply = Profile.supplies[Lib.RandomInt(Profile.supplies.Count)]; res = ResourceCache.Info(v, supply.resource); } // compile list of events with condition satisfied List <KerbalBreakdown> events = new List <KerbalBreakdown> { KerbalBreakdown.mumbling //< do nothing, here so there is always something that can happen }; if (Lib.HasData(v)) { events.Add(KerbalBreakdown.fat_finger); } if (Reliability.CanMalfunction(v)) { events.Add(KerbalBreakdown.rage); } if (res != null && res.amount > double.Epsilon) { events.Add(KerbalBreakdown.wrong_valve); } // choose a breakdown event KerbalBreakdown breakdown = events[Lib.RandomInt(events.Count)]; // generate message string text = ""; string subtext = ""; switch (breakdown) { case KerbalBreakdown.mumbling: text = "$ON_VESSEL$KERBAL has been in space for too long"; subtext = "Mumbling incoherently"; break; case KerbalBreakdown.fat_finger: text = "$ON_VESSEL$KERBAL is pressing buttons at random on the control panel"; subtext = "Science data has been lost"; break; case KerbalBreakdown.rage: text = "$ON_VESSEL$KERBAL is possessed by a blind rage"; subtext = "A component has been damaged"; break; case KerbalBreakdown.wrong_valve: text = "$ON_VESSEL$KERBAL opened the wrong valve"; subtext = res.resource_name + " has been lost"; break; } // post message first so this one is shown before malfunction message Message.Post(Severity.breakdown, Lib.ExpandMsg(text, v, c), subtext); // trigger the event switch (breakdown) { case KerbalBreakdown.mumbling: break; // do nothing case KerbalBreakdown.fat_finger: Lib.RemoveData(v); break; case KerbalBreakdown.rage: Reliability.CauseMalfunction(v); break; case KerbalBreakdown.wrong_valve: res.Consume(res.amount * res_penalty); break; } // remove reputation if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER) { Reputation.Instance.AddReputation(-PreferencesBasic.Instance.breakdownPenalty, TransactionReasons.Any); } }
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"); shrouded = part.ShieldedFromAirstream; string subject_id; issue = TestForIssues(vessel, exp, ec, this, broken, remainingSampleMass, didPrepare, shrouded, last_subject_id, out subject_id); if (last_subject_id != subject_id) { dataSampled = 0; } last_subject_id = subject_id; if (!recording) { return; } // if experiment is active and there are no issues if (issue.Length == 0 && dataSampled < exp.scienceCap * exp.dataScale) { // record in drive double elapsed = Kerbalism.elapsed_s; double chunkSize = data_rate * elapsed; var info = Science.Experiment(subject_id); double massDelta = sample_mass * chunkSize / info.max_amount; bool isSample = sample_mass < float.Epsilon; var drive = isSample ? DB.Vessel(vessel).BestDrive(chunkSize, 0) : DB.Vessel(vessel).BestDrive(0, Lib.SampleSizeToSlots(chunkSize)); // on high time warp this chunk size could be too big, but we could store a sizable amount if we processed less double maxCapacity = isSample ? drive.FileCapacityAvailable() : drive.SampleCapacityAvailable(subject_id); if (maxCapacity < chunkSize) { double factor = maxCapacity / chunkSize; chunkSize *= factor; massDelta *= factor; elapsed *= factor; } bool stored = false; if (sample_mass < float.Epsilon) { stored = drive.Record_file(subject_id, chunkSize, true, true); } else { stored = drive.Record_sample(subject_id, chunkSize, massDelta); } if (stored) { // consume ec ec.Consume(ec_rate * elapsed); dataSampled += chunkSize; dataSampled = Math.Min(dataSampled, exp.scienceCap * exp.dataScale); if (!sample_collecting) { remainingSampleMass -= massDelta; remainingSampleMass = Math.Max(remainingSampleMass, 0); } } else { issue = "insufficient storage"; } } }
private static bool DoRecord(Experiment experiment, string subject_id, Vessel vessel, Resource_info ec, uint hdId, Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs, double remainingSampleMass, double dataSampled, out double sampledOut, out double remainingSampleMassOut) { var exp = Science.Experiment(subject_id); if (Done(exp, dataSampled)) { sampledOut = dataSampled; remainingSampleMassOut = remainingSampleMass; return(true); } double elapsed = Kerbalism.elapsed_s; double chunkSize = Math.Min(experiment.data_rate * elapsed, exp.max_amount); double massDelta = experiment.sample_mass * chunkSize / exp.max_amount; Drive drive = GetDrive(experiment, vessel, hdId, chunkSize, subject_id); // on high time warp this chunk size could be too big, but we could store a sizable amount if we process less bool isFile = experiment.sample_mass < float.Epsilon; double maxCapacity = isFile ? drive.FileCapacityAvailable() : drive.SampleCapacityAvailable(subject_id); if (maxCapacity < chunkSize) { double factor = maxCapacity / chunkSize; chunkSize *= factor; massDelta *= factor; elapsed *= factor; } foreach (var p in resourceDefs) { resources.Consume(vessel, p.Key, p.Value * elapsed, "experiment"); } bool stored = false; if (isFile) { stored = drive.Record_file(subject_id, chunkSize, true); } else { stored = drive.Record_sample(subject_id, chunkSize, massDelta); } if (stored) { // consume ec ec.Consume(experiment.ec_rate * elapsed, "experiment"); dataSampled += chunkSize; dataSampled = Math.Min(dataSampled, exp.max_amount); sampledOut = dataSampled; if (!experiment.sample_collecting) { remainingSampleMass -= massDelta; remainingSampleMass = Math.Max(remainingSampleMass, 0); } remainingSampleMassOut = remainingSampleMass; return(true); } sampledOut = dataSampled; remainingSampleMassOut = remainingSampleMass; return(false); }
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); } } }
public void FixedUpdate() { // do nothing in the editor if (Lib.IsEditor()) { return; } // if enabled and not ready for harvest if (active && growth < 0.99) { // get vessel info from the cache // - if the vessel is not valid (eg: flagged as debris) then solar flux will be 0 and landed false (but that's okay) Vessel_info vi = Cache.VesselInfo(vessel); // get resource cache Vessel_resources resources = ResourceCache.Get(vessel); Resource_info ec = resources.Info(vessel, "ElectricCharge"); // deal with corner cases when greenhouse is assembled using KIS if (double.IsNaN(growth) || double.IsInfinity(growth)) { growth = 0.0; } // calculate natural and artificial lighting natural = vi.solar_flux; artificial = Math.Max(light_tolerance - natural, 0.0); // consume EC for the lamps, scaled by artificial light intensity if (artificial > double.Epsilon) { ec.Consume(ec_rate * (artificial / light_tolerance) * Kerbalism.elapsed_s, "greenhouse"); } // reset artificial lighting if there is no ec left // - comparing against amount in previous simulation step if (ec.amount <= double.Epsilon) { artificial = 0.0; } // execute recipe Resource_recipe recipe = new Resource_recipe(part, "greenhouse"); foreach (ModuleResource input in resHandler.inputResources) { // WasteAtmosphere is primary combined input if (WACO2 && input.name == "WasteAtmosphere") { recipe.Input(input.name, vi.breathable ? 0.0 : input.rate * Kerbalism.elapsed_s, "CarbonDioxide"); } // CarbonDioxide is secondary combined input else if (WACO2 && input.name == "CarbonDioxide") { recipe.Input(input.name, vi.breathable ? 0.0 : input.rate * Kerbalism.elapsed_s, ""); } // if atmosphere is breathable disable WasteAtmosphere / CO2 else if (!WACO2 && (input.name == "CarbonDioxide" || input.name == "WasteAtmosphere")) { recipe.Input(input.name, vi.breathable ? 0.0 : input.rate, ""); } else { recipe.Input(input.name, input.rate * Kerbalism.elapsed_s); } } foreach (ModuleResource output in resHandler.outputResources) { // if atmosphere is breathable disable Oxygen if (output.name == "Oxygen") { recipe.Output(output.name, vi.breathable ? 0.0 : output.rate * Kerbalism.elapsed_s, true); } else { recipe.Output(output.name, output.rate * Kerbalism.elapsed_s, true); } } resources.Transform(recipe); // determine environment conditions bool lighting = natural + artificial >= light_tolerance; bool pressure = pressure_tolerance <= double.Epsilon || vi.pressure >= pressure_tolerance; bool radiation = radiation_tolerance <= double.Epsilon || vi.radiation * (1.0 - vi.shielding) < radiation_tolerance; // determine input resources conditions // - comparing against amounts in previous simulation step bool inputs = true; string missing_res = string.Empty; bool dis_WACO2 = false; foreach (ModuleResource input in resHandler.inputResources) { // combine WasteAtmosphere and CO2 if both exist if (input.name == "WasteAtmosphere" || input.name == "CarbonDioxide") { if (dis_WACO2 || Cache.VesselInfo(vessel).breathable) { continue; // skip if already checked or atmosphere is breathable } if (WACO2) { if (resources.Info(vessel, "WasteAtmosphere").amount <= double.Epsilon && resources.Info(vessel, "CarbonDioxide").amount <= double.Epsilon) { inputs = false; missing_res = "CarbonDioxide"; break; } dis_WACO2 = true; continue; } } if (resources.Info(vessel, input.name).amount <= double.Epsilon) { inputs = false; missing_res = input.name; break; } } // if growing if (lighting && pressure && radiation && inputs) { // increase growth growth += crop_rate * Kerbalism.elapsed_s; growth = Math.Min(growth, 1.0); // notify the user when crop can be harvested if (growth >= 0.99) { Message.Post(Lib.BuildString("On <b>", vessel.vesselName, "</b> the crop is ready to be harvested")); growth = 1.0; } } // update time-to-harvest tta = (1.0 - growth) / crop_rate; // update issues issue = !inputs?Lib.BuildString("missing ", missing_res) : !lighting ? "insufficient lighting" : !pressure ? "insufficient pressure" : !radiation ? "excessive radiation" : string.Empty; } }
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); } }
public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Greenhouse g, Vessel_info vi, Vessel_resources resources, double elapsed_s) { // get protomodule data bool active = Lib.Proto.GetBool(m, "active"); double growth = Lib.Proto.GetDouble(m, "growth"); // if enabled and not ready for harvest if (active && growth < 0.99) { // get resource handler Resource_info ec = resources.Info(v, "ElectricCharge"); // calculate natural and artificial lighting double natural = vi.solar_flux; double artificial = Math.Max(g.light_tolerance - natural, 0.0); // consume EC for the lamps, scaled by artificial light intensity if (artificial > double.Epsilon) { ec.Consume(g.ec_rate * (artificial / g.light_tolerance) * elapsed_s, "greenhouse"); } // reset artificial lighting if there is no ec left // note: comparing against amount in previous simulation step if (ec.amount <= double.Epsilon) { artificial = 0.0; } // execute recipe Resource_recipe recipe = new Resource_recipe(g.part, "greenhouse"); foreach (ModuleResource input in g.resHandler.inputResources) //recipe.Input(input.name, input.rate * elapsed_s); { // WasteAtmosphere is primary combined input if (g.WACO2 && input.name == "WasteAtmosphere") { recipe.Input(input.name, vi.breathable ? 0.0 : input.rate * elapsed_s, "CarbonDioxide"); } // CarbonDioxide is secondary combined input else if (g.WACO2 && input.name == "CarbonDioxide") { recipe.Input(input.name, vi.breathable ? 0.0 : input.rate * elapsed_s, ""); } // if atmosphere is breathable disable WasteAtmosphere / CO2 else if (!g.WACO2 && (input.name == "CarbonDioxide" || input.name == "WasteAtmosphere")) { recipe.Input(input.name, vi.breathable ? 0.0 : input.rate, ""); } else { recipe.Input(input.name, input.rate * elapsed_s); } } foreach (ModuleResource output in g.resHandler.outputResources) { // if atmosphere is breathable disable Oxygen if (output.name == "Oxygen") { recipe.Output(output.name, vi.breathable ? 0.0 : output.rate * elapsed_s, true); } else { recipe.Output(output.name, output.rate * elapsed_s, true); } } resources.Transform(recipe); // determine environment conditions bool lighting = natural + artificial >= g.light_tolerance; bool pressure = g.pressure_tolerance <= double.Epsilon || vi.pressure >= g.pressure_tolerance; bool radiation = g.radiation_tolerance <= double.Epsilon || vi.radiation * (1.0 - vi.shielding) < g.radiation_tolerance; // determine inputs conditions // note: comparing against amounts in previous simulation step bool inputs = true; string missing_res = string.Empty; bool dis_WACO2 = false; foreach (ModuleResource input in g.resHandler.inputResources) { // combine WasteAtmosphere and CO2 if both exist if (input.name == "WasteAtmosphere" || input.name == "CarbonDioxide") { if (dis_WACO2 || vi.breathable) { continue; // skip if already checked or atmosphere is breathable } if (g.WACO2) { if (resources.Info(v, "WasteAtmosphere").amount <= double.Epsilon && resources.Info(v, "CarbonDioxide").amount <= double.Epsilon) { inputs = false; missing_res = "CarbonDioxide"; break; } dis_WACO2 = true; continue; } } if (resources.Info(v, input.name).amount <= double.Epsilon) { inputs = false; missing_res = input.name; break; } } // if growing if (lighting && pressure && radiation && inputs) { // increase growth growth += g.crop_rate * elapsed_s; growth = Math.Min(growth, 1.0); // notify the user when crop can be harvested if (growth >= 0.99) { Message.Post(Lib.BuildString("On <b>", v.vesselName, "</b> the crop is ready to be harvested")); growth = 1.0; } } // update time-to-harvest double tta = (1.0 - growth) / g.crop_rate; // update issues string issue = !inputs?Lib.BuildString("missing ", missing_res) : !lighting ? "insufficient lighting" : !pressure ? "insufficient pressure" : !radiation ? "excessive radiation" : string.Empty; // update protomodule data Lib.Proto.Set(m, "natural", natural); Lib.Proto.Set(m, "artificial", artificial); Lib.Proto.Set(m, "tta", tta); Lib.Proto.Set(m, "issue", issue); Lib.Proto.Set(m, "growth", growth); } }
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>")); } } } }