// execute a script public void Execute(Vessel v, ScriptType type) { // do nothing if there is no EC left on the vessel Resource_info ec = ResourceCache.Info(v, "ElectricCharge"); if (ec.amount <= double.Epsilon) { return; } // get the script Script script; if (scripts.TryGetValue(type, out script)) { // execute the script script.Execute(Boot(v)); // show message to the user // - unless the script is empty (can happen when being edited) if (script.states.Count > 0 && DB.Vessel(v).cfg_script) { Message.Post(Lib.BuildString(Localizer.Format("#KERBALISM_UI_scriptvessel"), " <b>", v.vesselName, "</b>")); } } }
public KeyValuePair <bool, double> modReturn; // Return from DeviceEC public override void OnStart(StartState state) { // don't break tutorial scenarios & do something only in Flight scenario if (Lib.DisableScenario(this) || !Lib.IsFlight()) { return; } // cache list of modules module = part.FindModulesImplementing <PartModule>().FindLast(k => k.moduleName == type); // get energy from cache resources = ResourceCache.Info(vessel, "ElectricCharge"); hasEnergy = resources.amount > double.Epsilon; // Force the update to run at least once lastBrokenState = !isBroken; hasEnergyChanged = !hasEnergy; hasFixedEnergyChanged = !hasEnergy; #if DEBUG // setup UI Fields["actualCost"].guiActive = true; Fields["isBroken"].guiActive = true; #endif }
void Indicator_ec(Panel p, Vessel v, Vessel_info vi) { #if !KSP170 && !KSP16 && !KSP15 && !KSP14 if (v.vesselType == VesselType.DeployedScienceController) { return; } #endif Resource_info ec = ResourceCache.Info(v, "ElectricCharge"); Supply supply = Profile.supplies.Find(k => k.resource == "ElectricCharge"); double low_threshold = supply != null ? supply.low_threshold : 0.15; double depletion = ec.Depletion(vi.crew_count); string tooltip = Lib.BuildString ( "<align=left /><b>name\tlevel\tduration</b>\n", ec.level <= 0.005 ? "<color=#ff0000>" : ec.level <= low_threshold ? "<color=#ffff00>" : "<color=#cccccc>", "EC\t", Lib.HumanReadablePerc(ec.level), "\t", depletion <= double.Epsilon ? "depleted" : Lib.HumanReadableDuration(depletion), "</color>" ); Texture2D image = ec.level <= 0.005 ? Icons.battery_red : ec.level <= low_threshold ? Icons.battery_yellow : Icons.battery_white; p.AddIcon(image, tooltip); }
// return total radiation emitted in a vessel public static double Total(Vessel v) { // get resource cache Resource_info ec = ResourceCache.Info(v, "ElectricCharge"); double tot = 0.0; if (v.loaded) { foreach (var emitter in Lib.FindModules <Emitter>(v)) { if (ec.amount > double.Epsilon || emitter.ec_rate <= double.Epsilon) { tot += emitter.running ? emitter.radiation : 0.0; } } } else { foreach (ProtoPartModuleSnapshot m in Lib.FindModules(v.protoVessel, "Emitter")) { if (ec.amount > double.Epsilon || Lib.Proto.GetDouble(m, "ec_rate") <= double.Epsilon) { tot += Lib.Proto.GetBool(m, "running") ? Lib.Proto.GetDouble(m, "radiation") : 0.0; } } } return(tot); }
static void Render_supplies(Panel p, Vessel v, Vessel_info vi, Vessel_resources resources) { // for each supply int supplies = 0; foreach (Supply supply in Profile.supplies) { // get resource info Resource_info res = resources.Info(v, supply.resource); // only show estimate if the resource is present if (res.amount <= 1e-10) continue; // render panel title, if not done already if (supplies == 0) p.AddSection("SUPPLIES"); // rate tooltip string rate_tooltip = Math.Abs(res.rate) >= 1e-10 ? Lib.BuildString ( res.rate > 0.0 ? "<color=#00ff00><b>" : "<color=#ffaa00><b>", Lib.HumanReadableRate(Math.Abs(res.rate)), "</b></color>" ) : string.Empty; // determine label string label = supply.resource == "ElectricCharge" ? "battery" : Lib.SpacesOnCaps(supply.resource).ToLower(); // finally, render resource supply p.AddContent(label, Lib.HumanReadableDuration(res.Depletion(vi.crew_count)), rate_tooltip); ++supplies; } }
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; } }
public void FixedUpdate() { if (!loaded) { return; } Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); RunProcessTick(vessel, Kerbalism.elapsed_s, ec_produced, resourcesProduced, ec_consumed, resourcesConsumed, ec, ResourceCache.Get(vessel)); }
static void ProcessConverter(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceConverter converter, Vessel_resources resources, double elapsed_s) { // note: ignore stock temperature mechanic of converters // note: ignore auto shutdown // note: non-mandatory resources 'dynamically scale the ratios', that is exactly what mandatory resources do too (DERP ALERT) // note: 'undo' stock behavior by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // determine if vessel is full of all output resources // note: comparing against previous amount bool full = true; foreach (var or in converter.outputList) { Resource_info res = resources.Info(v, or.ResourceName); full &= (res.level >= converter.FillAmount - double.Epsilon); } // if not full if (!full) { // deduce crew bonus int exp_level = -1; if (converter.UseSpecialistBonus) { foreach (ProtoCrewMember c in Lib.CrewList(v)) { if (c.experienceTrait.Effects.Find(k => k.Name == converter.ExperienceEffect) != null) { exp_level = Math.Max(exp_level, c.experienceLevel); } } } double exp_bonus = exp_level < 0 ? converter.EfficiencyBonus * converter.SpecialistBonusBase : converter.EfficiencyBonus * (converter.SpecialistBonusBase + (converter.SpecialistEfficiencyFactor * (exp_level + 1))); // create and commit recipe Resource_recipe recipe = new Resource_recipe(p); foreach (var ir in converter.inputList) { recipe.Input(ir.ResourceName, ir.Ratio * exp_bonus * elapsed_s); } foreach (var or in converter.outputList) { recipe.Output(or.ResourceName, or.Ratio * exp_bonus * elapsed_s, or.DumpExcess); } resources.Transform(recipe); } // undo stock behavior by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
private void DoRecord(Resource_info ec, string subject_id) { var stored = DoRecord(this, subject_id, vessel, ec, privateHdId, ResourceCache.Get(vessel), resourceDefs, remainingSampleMass, dataSampled, out dataSampled, out remainingSampleMass); if (!stored) { issue = insufficient_storage; } }
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 override void OnUpdate() { if (!Lib.IsFlight() || module == null) { return; } // get energy from cache resources = ResourceCache.Info(vessel, "ElectricCharge"); hasEnergy = resources.amount > double.Epsilon; // Update UI only if hasEnergy has changed or if is broken state has changed if (isBroken) { if (isBroken != lastBrokenState) { lastBrokenState = isBroken; Update_UI(!isBroken); } } else if (hasEnergyChanged != hasEnergy) { Lib.DebugLog("Energy state has changed: {0}", hasEnergy); hasEnergyChanged = hasEnergy; lastBrokenState = false; // Update UI Update_UI(hasEnergy); } // Constantly Update UI for special modules if (isBroken) { Constant_OnGUI(!isBroken); } else { Constant_OnGUI(hasEnergy); } if (!hasEnergy || isBroken) { actualCost = 0; isConsuming = false; } else { isConsuming = GetIsConsuming(); } }
public virtual void Update() { if (Lib.IsFlight() && Features.Deploy) { // get ec resource handler resourceInfo = ResourceCache.Info(vessel, "ElectricCharge"); hasEC = resourceInfo.amount > double.Epsilon; isConsuming = GetisConsuming; if (!isConsuming) { actualECCost = 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); } }
public void Execute(Vessel v, VesselData vd, Vessel_resources resources) { // get crew List <ProtoCrewMember> crew = Lib.CrewList(v); // get resource handler Resource_info res = resources.Info(v, resource); // get data from db SupplyData sd = DB.Vessel(v).Supply(resource); // message obey user config bool show_msg = resource == "ElectricCharge" ? vd.cfg_ec : vd.cfg_supply; // messages are shown only if there is some capacity and the vessel is manned // special case: ElectricCharge related messages are shown for unmanned vessels too if (res.capacity > double.Epsilon && (crew.Count > 0 || resource == "ElectricCharge")) { // manned/probe message variant uint variant = crew.Count > 0 ? 0 : 1u; // manage messages if (res.level <= double.Epsilon && sd.message < 2) { if (empty_message.Length > 0 && show_msg) { Message.Post(Severity.danger, Lib.ExpandMsg(empty_message, v, null, variant)); } sd.message = 2; } else if (res.level < low_threshold && sd.message < 1) { if (low_message.Length > 0 && show_msg) { Message.Post(Severity.warning, Lib.ExpandMsg(low_message, v, null, variant)); } sd.message = 1; } else if (res.level > low_threshold && sd.message > 0) { if (refill_message.Length > 0 && show_msg) { Message.Post(Severity.relax, Lib.ExpandMsg(refill_message, v, null, variant)); } sd.message = 0; } } }
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 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); } }
// return a resource handler public Resource_info Info(Vessel v, string resource_name) { // try to get existing entry if any Resource_info res; if (resources.TryGetValue(resource_name, out res)) { return(res); } // create new entry res = new Resource_info(v, resource_name); // remember new entry resources.Add(resource_name, res); // return new entry return(res); }
void Indicator_supplies(Panel p, Vessel v, Vessel_info vi) { List <string> tooltips = new List <string>(); uint max_severity = 0; if (vi.crew_count > 0) { foreach (Supply supply in Profile.supplies.FindAll(k => k.resource != "ElectricCharge")) { Resource_info res = ResourceCache.Info(v, supply.resource); double depletion = res.Depletion(vi.crew_count); if (res.capacity > double.Epsilon) { if (tooltips.Count == 0) { tooltips.Add(String.Format("<align=left /><b>{0,-18}\tlevel\tduration</b>", "name")); } tooltips.Add(Lib.BuildString ( res.level <= 0.005 ? "<color=#ff0000>" : res.level <= supply.low_threshold ? "<color=#ffff00>" : "<color=#cccccc>", String.Format("{0,-18}\t{1}\t{2}", supply.resource, Lib.HumanReadablePerc(res.level), depletion <= double.Epsilon ? "depleted" : Lib.HumanReadableDuration(depletion)), "</color>" )); uint severity = res.level <= 0.005 ? 2u : res.level <= supply.low_threshold ? 1u : 0; max_severity = Math.Max(max_severity, severity); } } } Texture2D image = max_severity == 2 ? Icons.box_red : max_severity == 1 ? Icons.box_yellow : Icons.box_white; p.AddIcon(image, string.Join("\n", tooltips.ToArray())); }
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; } }
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); } }
private static string TestForIssues(Vessel v, Resource_info ec, Experiment experiment, uint hdId, bool broken, double remainingSampleMass, bool didPrepare, bool isShrouded, string last_subject_id) { var subject_id = Science.Generate_subject_id(experiment.experiment_id, v); if (broken) { return("broken"); } if (isShrouded && !experiment.allow_shrouded) { return("shrouded"); } bool needsReset = experiment.crew_reset.Length > 0 && !string.IsNullOrEmpty(last_subject_id) && subject_id != last_subject_id; if (needsReset) { return("reset required"); } if (ec.amount < double.Epsilon && experiment.ec_rate > double.Epsilon) { return("no Electricity"); } if (!string.IsNullOrEmpty(experiment.crew_operate)) { var cs = new CrewSpecs(experiment.crew_operate); if (!cs && Lib.CrewCount(v) > 0) { return("crew on board"); } else if (cs && !cs.Check(v)) { return(cs.Warning()); } } if (!experiment.sample_collecting && remainingSampleMass < double.Epsilon && experiment.sample_mass > double.Epsilon) { return("depleted"); } if (!didPrepare && !string.IsNullOrEmpty(experiment.crew_prepare)) { return("not prepared"); } string situationIssue = Science.TestRequirements(experiment.experiment_id, experiment.requires, v); if (situationIssue.Length > 0) { return(Science.RequirementText(situationIssue)); } var experimentSize = Science.Experiment(subject_id).max_amount; double chunkSize = Math.Min(experiment.data_rate * Kerbalism.elapsed_s, experimentSize); Drive drive = GetDrive(experiment, v, hdId, chunkSize, subject_id); var isFile = experiment.sample_mass < double.Epsilon; double available = 0; if (isFile) { available = drive.FileCapacityAvailable(); available += Cache.WarpCache(v).FileCapacityAvailable(); } else { available = drive.SampleCapacityAvailable(subject_id); } if (Math.Min(experiment.data_rate * Kerbalism.elapsed_s, experimentSize) > available) { return(insufficient_storage); } return(string.Empty); }
public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Experiment experiment, Resource_info ec, Vessel_resources resources, double elapsed_s) { 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); bool forcedRun = Lib.Proto.GetBool(m, "forcedRun", false); bool recording = Lib.Proto.GetBool(m, "recording", false); uint privateHdId = Lib.Proto.GetUInt(m, "privateHdId", 0); string issue = TestForIssues(v, ec, experiment, privateHdId, broken, remainingSampleMass, didPrepare, shrouded, last_subject_id); if (string.IsNullOrEmpty(issue)) { issue = TestForResources(v, KerbalismProcess.ParseResources(experiment.resources), elapsed_s, resources); } Lib.Proto.Set(m, "issue", issue); if (!string.IsNullOrEmpty(issue)) { return; } var subject_id = Science.Generate_subject_id(experiment.experiment_id, v); Lib.Proto.Set(m, "last_subject_id", subject_id); double dataSampled = Lib.Proto.GetDouble(m, "dataSampled"); if (last_subject_id != subject_id) { dataSampled = 0; Lib.Proto.Set(m, "forcedRun", false); } double scienceValue = Science.Value(last_subject_id); Lib.Proto.Set(m, "scienceValue", scienceValue); var state = GetState(scienceValue, issue, recording, forcedRun); if (state != State.RUNNING) { return; } if (dataSampled >= Science.Experiment(subject_id).max_amount) { return; } var stored = DoRecord(experiment, subject_id, v, ec, privateHdId, resources, KerbalismProcess.ParseResources(experiment.resources), remainingSampleMass, dataSampled, out dataSampled, out remainingSampleMass); if (!stored) { Lib.Proto.Set(m, "issue", insufficient_storage); } Lib.Proto.Set(m, "dataSampled", dataSampled); Lib.Proto.Set(m, "remainingSampleMass", remainingSampleMass); }
private static double Rate(Vessel v, double chunkSize, double maxCapacity, double elapsed, Resource_info ec, double ec_rate, Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs) { double result = Lib.Clamp(maxCapacity / chunkSize, 0, 1); result = Math.Min(result, Lib.Clamp(ec.amount / (ec_rate * elapsed), 0, 1)); foreach (var p in resourceDefs) { var ri = resources.Info(v, p.Key); result = Math.Min(result, Lib.Clamp(ri.amount / (p.Value * elapsed), 0, 1)); } return(result); }
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); }
// call scripts automatically when conditions are met public void Automate(Vessel v, Vessel_info vi, Vessel_resources resources) { // do nothing if automation is disabled if (!Features.Automation) { return; } // get current states Resource_info ec = resources.Info(v, "ElectricCharge"); bool sunlight = vi.sunlight > double.Epsilon; bool power_low = ec.level < 0.2; bool power_high = ec.level > 0.8; bool radiation_low = vi.radiation < 0.000005552; //< 0.02 rad/h bool radiation_high = vi.radiation > 0.00001388; //< 0.05 rad/h bool signal = vi.connection.linked; // get current situation bool landed = false; bool atmo = false; bool space = false; switch (v.situation) { case Vessel.Situations.LANDED: case Vessel.Situations.SPLASHED: landed = true; break; case Vessel.Situations.FLYING: atmo = true; break; case Vessel.Situations.SUB_ORBITAL: case Vessel.Situations.ORBITING: case Vessel.Situations.ESCAPING: space = true; break; } // compile list of scripts that need to be called var to_exec = new List <Script>(); foreach (var p in scripts) { ScriptType type = p.Key; Script script = p.Value; if (script.states.Count == 0) { continue; //< skip empty scripts (may happen during editing) } switch (type) { case ScriptType.landed: if (landed && script.prev == "0") { to_exec.Add(script); } script.prev = landed ? "1" : "0"; break; case ScriptType.atmo: if (atmo && script.prev == "0") { to_exec.Add(script); } script.prev = atmo ? "1" : "0"; break; case ScriptType.space: if (space && script.prev == "0") { to_exec.Add(script); } script.prev = space ? "1" : "0"; break; case ScriptType.sunlight: if (sunlight && script.prev == "0") { to_exec.Add(script); } script.prev = sunlight ? "1" : "0"; break; case ScriptType.shadow: if (!sunlight && script.prev == "0") { to_exec.Add(script); } script.prev = !sunlight ? "1" : "0"; break; case ScriptType.power_high: if (power_high && script.prev == "0") { to_exec.Add(script); } script.prev = power_high ? "1" : "0"; break; case ScriptType.power_low: if (power_low && script.prev == "0") { to_exec.Add(script); } script.prev = power_low ? "1" : "0"; break; case ScriptType.rad_low: if (radiation_low && script.prev == "0") { to_exec.Add(script); } script.prev = radiation_low ? "1" : "0"; break; case ScriptType.rad_high: if (radiation_high && script.prev == "0") { to_exec.Add(script); } script.prev = radiation_high ? "1" : "0"; break; case ScriptType.linked: if (signal && script.prev == "0") { to_exec.Add(script); } script.prev = signal ? "1" : "0"; break; case ScriptType.unlinked: if (!signal && script.prev == "0") { to_exec.Add(script); } script.prev = !signal ? "1" : "0"; break; } } // if there are scripts to call if (to_exec.Count > 0) { // get list of devices // - we avoid creating it when there are no scripts to be executed, making its overall cost trivial var devices = Boot(v); // execute all scripts foreach (Script script in to_exec) { script.Execute(devices); } // show message to the user if (DB.Vessel(v).cfg_script) { Message.Post(Lib.BuildString("Script called on vessel <b>", v.vesselName, "</b>")); } } }
public void FixedUpdate() { // basic sanity checks if (Lib.IsEditor()) { return; } if (!Cache.VesselInfo(vessel).is_valid) { return; } if (next_check > Planetarium.GetUniversalTime()) { return; } // get ec handler Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); shrouded = part.ShieldedFromAirstream; issue = TestForIssues(vessel, ec, this, privateHdId, broken, remainingSampleMass, didPrepare, shrouded, last_subject_id); if (string.IsNullOrEmpty(issue)) { issue = TestForResources(vessel, resourceDefs, Kerbalism.elapsed_s, ResourceCache.Get(vessel)); } scienceValue = Science.Value(last_subject_id, 0); state = GetState(scienceValue, issue, recording, forcedRun); if (!string.IsNullOrEmpty(issue)) { next_check = Planetarium.GetUniversalTime() + Math.Max(3, Kerbalism.elapsed_s * 3); return; } var subject_id = Science.Generate_subject_id(experiment_id, vessel); if (last_subject_id != subject_id) { dataSampled = 0; forcedRun = false; } last_subject_id = subject_id; if (state != State.RUNNING) { return; } var exp = Science.Experiment(experiment_id); if (dataSampled >= exp.max_amount) { return; } // if experiment is active and there are no issues DoRecord(ec, subject_id); }
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; } }
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, 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 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); } }