// return quality-of-life bonus public static double Bonus(Vessel v) { // deduce crew count and capacity int crew_count = Lib.CrewCount(v); int crew_capacity = Lib.CrewCapacity(v); // deduce entertainment bonus, multiplying all entertainment factors double entertainment = 1.0; if (v.loaded) { foreach(Entertainment m in v.FindPartModulesImplementing<Entertainment>()) { entertainment *= m.rate; } } else { foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots) { foreach(ProtoPartModuleSnapshot m in part.modules) { if (m.moduleName == "Entertainment") entertainment *= Lib.GetProtoValue<double>(m, "rate"); } } } // calculate quality of life bonus return Bonus((uint)crew_count, (uint)crew_capacity, entertainment, Lib.Landed(v), Signal.Link(v).linked); }
/// <summary> This ctor is to be used for newly created vessels </summary> public VesselData(Vessel vessel) { UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.Ctor"); ExistsInFlight = true; // vessel exists IsSimulated = false; // will be evaluated in next fixedupdate Vessel = vessel; VesselId = Vessel.id; parts = new Dictionary <uint, PartData>(); if (Vessel.loaded) { foreach (Part part in Vessel.Parts) { parts.Add(part.flightID, new PartData(part)); } } else { // vessels can be created unloaded, asteroids for example foreach (ProtoPartSnapshot protopart in Vessel.protoVessel.protoPartSnapshots) { parts.Add(protopart.flightID, new PartData(protopart)); } } FieldsDefaultInit(); cfg_storm |= Features.SpaceWeather && Lib.CrewCount(vessel) > 0; Lib.LogDebug("VesselData ctor (new vessel) : id '" + VesselId + "' (" + Vessel.vesselName + "), part count : " + parts.Count); UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> /// This ctor is meant to be used in OnLoad only, but can be used as a fallback /// with a null ConfigNode to create VesselData from a protovessel. /// The Vessel reference will be acquired in the next fixedupdate /// </summary> public VesselData(ProtoVessel protoVessel, ConfigNode node) { UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.Ctor"); ExistsInFlight = false; IsSimulated = false; VesselId = protoVessel.vesselID; parts = new Dictionary <uint, PartData>(); foreach (ProtoPartSnapshot protopart in protoVessel.protoPartSnapshots) { parts.Add(protopart.flightID, new PartData(protopart)); } if (node == null) { FieldsDefaultInit(); cfg_storm |= Features.SpaceWeather && Lib.CrewCount(protoVessel.vesselRef) > 0; Lib.LogDebug("VesselData ctor (created from protovessel) : id '" + VesselId + "' (" + protoVessel.vesselName + "), part count : " + parts.Count); } else { Load(node); Lib.LogDebug("VesselData ctor (loaded from database) : id '" + VesselId + "' (" + protoVessel.vesselName + "), part count : " + parts.Count); } UnityEngine.Profiling.Profiler.EndSample(); }
GUIContent indicator_supplies(Vessel v, vessel_info vi) { GUIContent state = new GUIContent(); List<string> tooltips = new List<string>(); uint max_severity = 0; if (Lib.CrewCount(v) > 0) { foreach(Rule r in Kerbalism.supply_rules) { var vmon = vi.vmon[r.name]; string deplete_str = vmon.depletion <= double.Epsilon ? ", depleted" : double.IsNaN(vmon.depletion) ? "" : ", deplete in <b>" + Lib.HumanReadableDuration(vmon.depletion) + "</b>"; tooltips.Add(r.resource_name + ": <b>" + (vmon.level * 100.0).ToString("F0") + "%</b>" + deplete_str); uint severity = vmon.level <= double.Epsilon ? 2u : vmon.level <= r.low_threshold ? 1u : 0; max_severity = Math.Max(max_severity, severity); } } switch(max_severity) { case 0: state.image = icon_supplies_nominal; break; case 1: state.image = icon_supplies_warning; break; case 2: state.image = icon_supplies_danger; break; } state.tooltip = string.Join("\n", tooltips.ToArray()); return state; }
void ToEVA(GameEvents.FromToAction <Part, Part> data) { OnVesselModified(data.from.vessel); OnVesselModified(data.to.vessel); // get total crew in the origin vessel double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0; // get vessel resources handler VesselResources resources = ResourceCache.Get(data.from.vessel); // setup supply resources capacity in the eva kerbal Profile.SetupEva(data.to); string evaPropName = Lib.EvaPropellantName(); // for each resource in the kerbal for (int i = 0; i < data.to.Resources.Count; ++i) { // get the resource PartResource res = data.to.Resources[i]; // eva prop is handled differently if (res.resourceName == evaPropName) { continue; } double quantity = Math.Min(resources.GetResource(data.from.vessel, res.resourceName).Amount / tot_crew, res.maxAmount); // remove resource from vessel quantity = data.from.RequestResource(res.resourceName, quantity); // add resource to eva kerbal data.to.RequestResource(res.resourceName, -quantity); } // Airlock loss resources.Consume(data.from.vessel, "Nitrogen", Settings.LifeSupportAtmoLoss, ResourceBroker.Generic); KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>(); // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva EVA.HeadLamps(kerbal, false); // execute script data.from.vessel.KerbalismData().computer.Execute(data.from.vessel, ScriptType.eva_out); // Start a coroutine for doing eva propellant resource transfers once the kerbal EVA is started (this is too early here) data.to.StartCoroutine(PostEVATweaks(data.from, data.to, evaPropName)); }
// estimate lifetime for the rule resource // note: this function must be called only once for simulation step // return 0 if no resource left, NaN if rate of change is positive, or seconds left in other cases public double EstimateLifetime(Vessel v) { // get prev amount for the vessel double prev_amount = 0.0; if (!prev_amounts.TryGetValue(v.id, out prev_amount)) prev_amount = 0.0; // calculate delta double amount = Lib.GetResourceAmount(v, this.resource_name); double meal_rate = this.interval > double.Epsilon ? this.rate / this.interval : 0.0; double delta = (amount - prev_amount) / TimeWarp.fixedDeltaTime - meal_rate * Lib.CrewCount(v); // remember prev amount prev_amounts[v.id] = amount; // return lifetime in seconds return amount <= double.Epsilon ? 0.0 : delta >= -double.Epsilon ? double.NaN : amount / -delta; }
private void EvaluateStatus() { UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.VesselData.EvaluateStatus"); // determine if there is enough EC for a powered state powered = Lib.IsPowered(Vessel); // calculate crew info for the vessel crewCount = Lib.CrewCount(Vessel); crewCapacity = Lib.CrewCapacity(Vessel); // malfunction stuff malfunction = Reliability.HasMalfunction(Vessel); critical = Reliability.HasCriticalFailure(Vessel); // communications info connection = ConnectionInfo.Update(Vessel, powered, EnvBlackout); // habitat data habitatInfo.Update(Vessel); volume = Habitat.Tot_volume(Vessel); surface = Habitat.Tot_surface(Vessel); pressure = Math.Min(Habitat.Pressure(Vessel), habitatInfo.MaxPressure); evas = (uint)(Math.Max(0, ResourceCache.GetResource(Vessel, "Nitrogen").Amount - 330) / Settings.LifeSupportAtmoLoss); poisoning = Habitat.Poisoning(Vessel); shielding = Habitat.Shielding(Vessel); livingSpace = Habitat.Living_space(Vessel); volumePerCrew = Habitat.Volume_per_crew(Vessel); comforts = new Comforts(Vessel, EnvLanded, crewCount > 1, connection.linked && connection.rate > double.Epsilon); // data about greenhouses greenhouses = Greenhouse.Greenhouses(Vessel); Drive.GetCapacity(this, out drivesFreeSpace, out drivesCapacity); // solar panels data if (Vessel.loaded) { solarPanelsAverageExposure = SolarPanelFixer.GetSolarPanelsAverageExposure(solarPanelsExposure); solarPanelsExposure.Clear(); } UnityEngine.Profiling.Profiler.EndSample(); }
void ToEVA(GameEvents.FromToAction <Part, Part> data) { // get total crew in the origin vessel double tot_crew = (double)Lib.CrewCount(data.from.vessel) + 1.0; // get vessel resources handler Vessel_resources resources = ResourceCache.Get(data.from.vessel); // setup supply resources capacity in the eva kerbal Profile.SetupEva(data.to); // for each resource in the kerbal for (int i = 0; i < data.to.Resources.Count; ++i) { // get the resource PartResource res = data.to.Resources[i]; // determine quantity to take double quantity = Math.Min(resources.Info(data.from.vessel, res.resourceName).amount / tot_crew, res.maxAmount); // remove resource from vessel quantity = data.from.RequestResource(res.resourceName, quantity); // add resource to eva kerbal data.to.RequestResource(res.resourceName, -quantity); } // show warning if there isn't monoprop in the eva suit string prop_name = Lib.EvaPropellantName(); if (Lib.Amount(data.to, prop_name) <= double.Epsilon && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", prop_name, "</b> in the EVA suit"), "Don't let the ladder go!"); } // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>(); EVA.HeadLamps(kerbal, false); // execute script DB.Vessel(data.from.vessel).computer.Execute(data.from.vessel, ScriptType.eva_out); }
// return seconds until depletion, 0.0 if there is no resource and NaN if it will never deplete public double Depletion(Vessel v, resource_info res) { // [unused] Newton–Raphson, 1 step // C = amount // R = delta // A = change in delta // t0 = C / -R // eyeball estimate // F' = 0.5 * t0 * t0 // differential of F // F = C + R * t0 + A * F' // equation of motion, constant acceleration // t1 = t0 - F / F' // time until depletion // calculate rate of change from interval-based rule double meal_rate = interval > double.Epsilon ? rate / interval : 0.0; // calculate total rate of change double delta = res.rate - meal_rate * Lib.CrewCount(v); // return depletion return res.amount <= double.Epsilon ? 0.0 : delta >= -0.000001 ? double.NaN : res.amount / -delta; }
public void Update() { // set lamps emissive object if (lamps_rdr != null) { float intensity = Lib.IsFlight() ? (active ? (float)(artificial / light_tolerance) : 0.0f) : (active ? 1.0f : 0.0f); lamps_rdr.material.SetColor("_EmissiveColor", new Color(intensity, intensity, intensity, 1.0f)); } // in flight if (Lib.IsFlight()) { // still-play plants animation if (plants_anim != null) { plants_anim.Still(growth); } // update ui string status = issue.Length > 0 ? Lib.BuildString("<color=yellow>", issue, "</color>") : growth > 0.99 ? "ready to harvest" : "growing"; Events["Toggle"].guiName = Lib.StatusToggle("Greenhouse", active ? status : "disabled"); Fields["status_natural"].guiActive = active && growth < 0.99; Fields["status_artificial"].guiActive = active && growth < 0.99; Fields["status_tta"].guiActive = active && growth < 0.99; status_natural = Lib.HumanReadableFlux(natural); status_artificial = Lib.HumanReadableFlux(artificial); status_tta = Lib.HumanReadableDuration(tta); // show/hide harvest buttons bool manned = FlightGlobals.ActiveVessel.isEVA || Lib.CrewCount(vessel) > 0; Events["Harvest"].active = manned && growth >= 0.99; Events["EmergencyHarvest"].active = manned && growth >= 0.5 && growth < 0.99; } // in editor else { // update ui Events["Toggle"].guiName = Lib.StatusToggle("Greenhouse", active ? "enabled" : "disabled"); } }
void Breakdown(Vessel v, ProtoCrewMember c) { // constants const double food_penality = 0.2; // proportion of food lost on 'depressed' const double oxygen_penality = 0.2; // proportion of oxygen lost on 'wrong_valve' // get info double food_amount = Lib.GetResourceAmount(v, "Food"); double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen"); // compile list of events with condition satisfied List<KerbalBreakdown> events = new List<KerbalBreakdown>(); events.Add(KerbalBreakdown.mumbling); //< do nothing, here so there is always something that can happen if (Lib.CrewCount(v) > 1) events.Add(KerbalBreakdown.argument); //< do nothing, add some variation to messages if (Lib.HasData(v)) events.Add(KerbalBreakdown.fat_finger); if (Malfunction.CanMalfunction(v)) events.Add(KerbalBreakdown.rage); if (food_amount > double.Epsilon) events.Add(KerbalBreakdown.depressed); if (oxygen_amount > double.Epsilon) events.Add(KerbalBreakdown.wrong_valve); // choose a breakdown event KerbalBreakdown breakdown = events[Lib.RandomInt(events.Count)]; // post message first so this one is shown before malfunction message Message.Post(Severity.breakdown, KerbalEvent.stress, v, c, breakdown); // trigger the event switch(breakdown) { case KerbalBreakdown.mumbling: break; // do nothing case KerbalBreakdown.argument: break; // do nothing case KerbalBreakdown.fat_finger: Lib.RemoveData(v); break; case KerbalBreakdown.rage: Malfunction.CauseMalfunction(v); break; case KerbalBreakdown.depressed: Lib.RequestResource(v, "Food", food_amount * food_penality); break; case KerbalBreakdown.wrong_valve: Lib.RequestResource(v, "Oxygen", oxygen_amount * oxygen_penality); break; } }
public void TransferData() { var hardDrives = vessel.FindPartModulesImplementing <HardDrive>(); foreach (var hardDrive in hardDrives) { if (hardDrive == this) { continue; } hardDrive.drive.Move(drive, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(vessel) > 0); } }
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); }
GUIContent indicator_supplies(Vessel v, List<Scrubber> scrubbers, List<Greenhouse> greenhouses) { // get food & oxygen info double food_amount = Lib.GetResourceAmount(v, "Food"); double food_capacity = Lib.GetResourceCapacity(v, "Food"); double food_level = food_capacity > 0.0 ? food_amount / food_capacity : 1.0; double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen"); double oxygen_capacity = Lib.GetResourceCapacity(v, "Oxygen"); double oxygen_level = oxygen_capacity > 0.0 ? oxygen_amount / oxygen_capacity : 1.0; double level = Math.Min(food_level, oxygen_level); // store the icon and tooltip GUIContent state = new GUIContent(); // choose an icon if (level <= Settings.ResourceDangerThreshold) state.image = icon_supplies_danger; else if (level <= Settings.ResourceWarningThreshold) state.image = icon_supplies_warning; else state.image = icon_supplies_nominal; // if there is someone on board List<string> tooltips = new List<string>(); int crew_count = Lib.CrewCount(v); if (crew_count > 0) { // get oxygen recycled by scrubbers double oxygen_recycled = 0.0; double ec_left = Lib.GetResourceAmount(v, "ElectricCharge"); double co2_left = Lib.GetResourceAmount(v, "CO2"); foreach(Scrubber scrubber in scrubbers) { if (scrubber.is_enabled) { double co2_consumed = Math.Max(co2_left, scrubber.co2_rate); ec_left -= scrubber.ec_rate; co2_left -= co2_consumed; if (ec_left > -double.Epsilon && co2_left > -double.Epsilon) oxygen_recycled += co2_consumed * scrubber.efficiency; else break; } } // calculate time until depletion for food double food_consumption = (double)crew_count * Settings.FoodPerMeal / Settings.MealFrequency; if (food_capacity > double.Epsilon && food_consumption > double.Epsilon) { double food_depletion = food_amount / food_consumption; tooltips.Add(food_amount / food_capacity > Settings.ResourceDangerThreshold ? "Food: <b>" + (food_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(food_depletion) + "</b>" : "Food: <b>depleted</b>"); } // calculate time until depletion for oxygen double oxygen_consumption = !LifeSupport.BreathableAtmosphere(v) ? (double)crew_count * Settings.OxygenPerSecond - oxygen_recycled : 0.0; if (oxygen_capacity > double.Epsilon && oxygen_consumption > double.Epsilon) { double oxygen_depletion = oxygen_amount / oxygen_consumption; tooltips.Add(oxygen_amount / oxygen_capacity > Settings.ResourceDangerThreshold ? "Oxygen: <b>" + (oxygen_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(oxygen_depletion) + "</b>" : "Oxygen: <b>depleted</b>"); } } state.tooltip = string.Join("\n", tooltips.ToArray()); return state; }
void ToEVA(GameEvents.FromToAction <Part, Part> data) { OnVesselModified(data.from.vessel); OnVesselModified(data.to.vessel); // get total crew in the origin vessel double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0; // get vessel resources handler VesselResources resources = ResourceCache.Get(data.from.vessel); // setup supply resources capacity in the eva kerbal Profile.SetupEva(data.to); String prop_name = Lib.EvaPropellantName(); // for each resource in the kerbal for (int i = 0; i < data.to.Resources.Count; ++i) { // get the resource PartResource res = data.to.Resources[i]; // eva prop is handled differently if (res.resourceName == prop_name) { continue; } double quantity = Math.Min(resources.GetResource(data.from.vessel, res.resourceName).Amount / tot_crew, res.maxAmount); // remove resource from vessel quantity = data.from.RequestResource(res.resourceName, quantity); // add resource to eva kerbal data.to.RequestResource(res.resourceName, -quantity); } // take as much of the propellant as possible. just imagine: there are 1.3 units left, and 12 occupants // in the ship. you want to send out an engineer to fix the chemical plant that produces monoprop, // and have to get from one end of the station to the other with just 0.1 units in the tank... // nope. double evaPropQuantity = data.from.RequestResource(prop_name, Lib.EvaPropellantCapacity()); // We can't just add the monoprop here, because that doesn't always work. It might be related // to the fact that stock KSP wants to add 5 units of monoprop to new EVAs. Instead of fighting KSP here, // we just let it do it's thing and set our amount later in EVA.cs - which seems to work just fine. // don't put that into Cache.VesselInfo because that can be deleted before we get there Cache.SetVesselObjectsCache(data.to.vessel, "eva_prop", evaPropQuantity); // Airlock loss resources.Consume(data.from.vessel, "Nitrogen", Settings.LifeSupportAtmoLoss, ResourceBroker.Generic); // show warning if there is little or no EVA propellant in the suit if (evaPropQuantity <= 0.05 && !Lib.Landed(data.from.vessel)) { Message.Post(Severity.danger, Local.CallBackMsg_EvaNoMP.Format("<b>" + prop_name + "</b>"), Local.CallBackMsg_EvaNoMP2); //Lib.BuildString("There isn't any <<1>> in the EVA suit")"Don't let the ladder go!" } // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>(); EVA.HeadLamps(kerbal, false); // execute script data.from.vessel.KerbalismData().computer.Execute(data.from.vessel, ScriptType.eva_out); }
public Vessel_info(Vessel v, UInt64 vessel_id, UInt64 inc) { // NOTE: anything used here can't in turn use cache, unless you know what you are doing // NOTE: you can't cache vessel position // at any point in time all vessel/body positions are relative to a different frame of reference // so comparing the current position of a vessel, with the cached one of another make no sense // associate with an unique incremental id this.inc = inc; // determine if this is a valid vessel is_vessel = Lib.IsVessel(v); if (!is_vessel) { return; } // determine if this is a rescue mission vessel is_rescue = Misc.IsRescueMission(v); if (is_rescue) { return; } // dead EVA are not valid vessels if (EVA.IsDead(v)) { return; } // shortcut for common tests is_valid = true; // generate id once id = vessel_id; // calculate crew info for the vessel crew_count = Lib.CrewCount(v); crew_capacity = Lib.CrewCapacity(v); // get vessel position Vector3d position = Lib.VesselPosition(v); // this should never happen again if (Vector3d.Distance(position, v.mainBody.position) < 1.0) { throw new Exception("Shit hit the fan for vessel " + v.vesselName); } // determine if there is enough EC for a powered state powered = ResourceCache.Info(v, "ElectricCharge").amount > double.Epsilon; // determine if in sunlight, calculate sun direction and distance sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0; // environment stuff atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir); gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude); underwater = Sim.Underwater(v); breathable = Sim.Breathable(v, underwater); landed = Lib.Landed(v); zerog = !landed && (!v.mainBody.atmosphere || v.mainBody.atmosphereDepth < v.altitude); if (v.mainBody.flightGlobalsIndex != 0 && TimeWarp.CurrentRate > 1000.0f) { highspeedWarp(v); } // temperature at vessel position temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux); temp_diff = Sim.TempDiff(temperature, v.mainBody, landed); // radiation radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out magnetosphere, out inner_belt, out outer_belt, out interstellar); // extended atmosphere thermosphere = Sim.InsideThermosphere(v); exosphere = Sim.InsideExosphere(v); // malfunction stuff malfunction = Reliability.HasMalfunction(v); critical = Reliability.HasCriticalFailure(v); // communications info connection = new ConnectionInfo(v, powered, blackout); transmitting = Science.Transmitting(v, connection.linked && connection.rate > double.Epsilon); // habitat data volume = Habitat.Tot_volume(v); surface = Habitat.Tot_surface(v); pressure = Habitat.Pressure(v); evas = (uint)(Math.Max(0, ResourceCache.Info(v, "Nitrogen").amount - 330) / PreferencesLifeSupport.Instance.evaAtmoLoss); poisoning = Habitat.Poisoning(v); humidity = Habitat.Humidity(v); shielding = Habitat.Shielding(v); living_space = Habitat.Living_space(v); volume_per_crew = Habitat.Volume_per_crew(v); comforts = new Comforts(v, landed, crew_count > 1, connection.linked && connection.rate > double.Epsilon); // data about greenhouses greenhouses = Greenhouse.Greenhouses(v); // other stuff gravioli = Sim.Graviolis(v); }
private void FieldsDefaultInit(ProtoVessel pv) { msg_signal = false; msg_belt = false; cfg_ec = PreferencesMessages.Instance.ec; cfg_supply = PreferencesMessages.Instance.supply; cfg_signal = PreferencesMessages.Instance.signal; cfg_malfunction = PreferencesMessages.Instance.malfunction; cfg_storm = Features.SpaceWeather && PreferencesMessages.Instance.storm && Lib.CrewCount(pv) > 0; cfg_script = PreferencesMessages.Instance.script; cfg_highlights = PreferencesReliability.Instance.highlights; cfg_showlink = true; cfg_show = true; deviceTransmit = true; stormData = new StormData(null); habitatInfo = new VesselHabitatInfo(null); computer = new Computer(null); supplies = new Dictionary <string, SupplyData>(); scansat_id = new List <uint>(); filesTransmitted = new List <File>(); vesselSituations = new VesselSituations(this); }
public static double Volume_per_crew(Vessel v) { // living space is the volume per-capita normalized against an 'ideal living space' and clamped in an acceptable range return(Tot_volume(v) / Math.Max(1, Lib.CrewCount(v))); }
// return living space factor in a vessel public static double Living_space(Vessel v) { // living space is the volume per-capita normalized against an 'ideal living space' and clamped in an acceptable range return(Lib.Clamp((Tot_volume(v) / Lib.CrewCount(v)) / PreferencesComfort.Instance.livingSpace, 0.1, 1.0)); }
public void StoreData() { // disable for dead eva kerbals Vessel v = FlightGlobals.ActiveVessel; if (v == null || EVA.IsDead(v)) { return; } // transfer data if (!Drive.Transfer(v, drive, PreferencesScience.Instance.sampleTransfer || Lib.CrewCount(v) > 0)) { Message.Post ( Lib.Color(Lib.BuildString("WARNING: not evering copied"), Lib.Kolor.Red, true), Lib.BuildString("Storage is at capacity") ); } }
public vessel_info(Vessel v, uint vessel_id, UInt64 inc) { // NOTE: anything used here can't in turn use cache, unless you know what you are doing // associate with an unique incremental id this.inc = inc; // determine if this is a valid vessel is_vessel = Lib.IsVessel(v); if (!is_vessel) return; // determine if this is a resque mission vessel is_resque = Lib.IsResqueMission(v); if (is_resque) return; // dead EVA are not valid vessels if (v.isEVA && EVA.KerbalData(v).eva_dead) return; // shortcut for common tests is_valid = true; // generate id once id = vessel_id; // calculate crew info for the vessel crew_count = Lib.CrewCount(v); crew_capacity = Lib.CrewCapacity(v); // get vessel position once position = Lib.VesselPosition(v); // determine if in sunlight, calculate sun direction and distance sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0; // if the orbit length vs simulation step is lower than an acceptable threshold, use discrete sun visibility if (v.mainBody.flightGlobalsIndex != 0) { double orbit_period = Sim.OrbitalPeriod(v); if (orbit_period / Kerbalism.elapsed_s < 16.0) sunlight = 1.0 - Sim.ShadowPeriod(v) / orbit_period; } // calculate environment stuff atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir); gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude); breathable = Sim.Breathable(v); landed = Lib.Landed(v); // calculate temperature at vessel position temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux); // calculate radiation radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out inside_pause, out inside_belt); // calculate malfunction stuff max_malfunction = Reliability.MaxMalfunction(v); avg_quality = Reliability.AverageQuality(v); // calculate signal info antenna = new antenna_data(v); avoid_inf_recursion.Add(v.id); link = Signal.Link(v, position, antenna, blackout, avoid_inf_recursion); avoid_inf_recursion.Remove(v.id); // partial data about modules, used by vessel info/monitor scrubbers = Scrubber.PartialData(v); recyclers = Recycler.PartialData(v); greenhouses = Greenhouse.PartialData(v); // woot relativity time_dilation = Sim.TimeDilation(v); }
// 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 info Rule supply = supply_rules.Count > 0 ? supply_rules[Lib.RandomInt(supply_rules.Count)] : null; resource_info res = supply != null?ResourceCache.Info(v, supply.resource_name) : null; // compile list of events with condition satisfied List <KerbalBreakdown> events = new List <KerbalBreakdown>(); events.Add(KerbalBreakdown.mumbling); //< do nothing, here so there is always something that can happen if (Lib.CrewCount(v) > 1) { events.Add(KerbalBreakdown.argument); //< do nothing, add some variation to messages } if (Lib.HasData(v)) { events.Add(KerbalBreakdown.fat_finger); } if (Reliability.CanMalfunction(v)) { events.Add(KerbalBreakdown.rage); } if (supply != null && res.amount > double.Epsilon) { events.Add(KerbalBreakdown.depressed); 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.argument: text = "$ON_VESSEL$KERBAL had an argument with the rest of the crew"; subtext = "Morale is degenerating at an alarming rate"; 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.depressed: text = "$ON_VESSEL$KERBAL is not respecting the rationing guidelines"; subtext = supply.resource_name + " has been lost"; break; case KerbalBreakdown.wrong_valve: text = "$ON_VESSEL$KERBAL opened the wrong valve"; subtext = supply.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.argument: break; // do nothing case KerbalBreakdown.fat_finger: Lib.RemoveData(v); break; case KerbalBreakdown.rage: Reliability.CauseMalfunction(v); break; case KerbalBreakdown.depressed: case KerbalBreakdown.wrong_valve: res.Consume(res.amount * res_penalty); break; } // remove reputation if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER) { Reputation.Instance.AddReputation(-Settings.BreakdownReputationPenalty, TransactionReasons.Any); } }
// implement radiation mechanics public void FixedUpdate() { // avoid case when DB isn't ready for whatever reason if (!DB.Ready()) { return; } // do nothing in the editors and the menus if (!Lib.SceneIsGame()) { return; } // do nothing if paused if (Lib.IsPaused()) { return; } // get time elapsed from last update double elapsed_s = TimeWarp.fixedDeltaTime; // for each vessel foreach (Vessel v in FlightGlobals.Vessels) { // skip invalid vessels if (!Lib.IsVessel(v)) { continue; } // skip dead eva kerbals if (EVA.IsDead(v)) { continue; } // get crew List <ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew(); // get crew count int crew_count = Lib.CrewCount(v); // get vessel info from the cache vessel_info info = Cache.VesselInfo(v); // get vessel data vessel_data vd = DB.VesselData(v.id); // belt warnings // note: we only show it for manned vesssels, but the first time we also show it for probes if (crew_count > 0 || DB.NotificationData().first_belt_crossing == 0) { if (InsideBelt(v) && vd.msg_belt < 1) { Message.Post("<b>" + v.vesselName + "</b> is crossing <i>" + v.mainBody.bodyName + " radiation belt</i>", "Exposed to extreme radiation"); vd.msg_belt = 1; DB.NotificationData().first_belt_crossing = 1; //< record first belt crossing } else if (!InsideBelt(v) && vd.msg_belt > 0) { // no message after crossing the belt vd.msg_belt = 0; } } // for each crew foreach (ProtoCrewMember c in crew) { // get kerbal data kerbal_data kd = DB.KerbalData(c.name); // skip resque kerbals if (kd.resque == 1) { continue; } // skip disabled kerbals if (kd.disabled == 1) { continue; } // accumulate radiation kd.radiation += info.radiation * elapsed_s; // kill kerbal if necessary if (kd.radiation >= Settings.RadiationFatalThreshold) { Message.Post(Severity.fatality, KerbalEvent.radiation, v, c); Kerbalism.Kill(v, c); } // show warnings else if (kd.radiation >= Settings.RadiationDangerThreshold && kd.msg_radiation < 2) { Message.Post(Severity.danger, KerbalEvent.radiation, v, c); kd.msg_radiation = 2; } else if (kd.radiation >= Settings.RadiationWarningThreshold && kd.msg_radiation < 1) { Message.Post(Severity.danger, KerbalEvent.radiation, v, c); kd.msg_radiation = 1; } // note: no recovery from radiations } } }
public vessel_info(Vessel v, uint vessel_id, UInt64 inc) { // NOTE: anything used here can't in turn use cache, unless you know what you are doing // NOTE: you can't cache vessel position // at any point in time all vessel/body positions are relative to a different frame of reference // so comparing the current position of a vessel, with the cached one of another make no sense // associate with an unique incremental id this.inc = inc; // determine if this is a valid vessel is_vessel = Lib.IsVessel(v); if (!is_vessel) return; // determine if this is a rescue mission vessel is_rescue = Misc.IsRescueMission(v); if (is_rescue) return; // dead EVA are not valid vessels if (EVA.IsDead(v)) return; // shortcut for common tests is_valid = true; // generate id once id = vessel_id; // calculate crew info for the vessel crew_count = Lib.CrewCount(v); crew_capacity = Lib.CrewCapacity(v); // get vessel position Vector3d position = Lib.VesselPosition(v); // this should never happen again if (Vector3d.Distance(position, v.mainBody.position) < 1.0) { throw new Exception("Shit hit the fan for vessel " + v.vesselName); } // determine if in sunlight, calculate sun direction and distance sunlight = Sim.RaytraceBody(v, position, FlightGlobals.Bodies[0], out sun_dir, out sun_dist) ? 1.0 : 0.0; // at the two highest timewarp speed, the number of sun visibility samples drop to the point that // the quantization error first became noticeable, and then exceed 100% // to solve this, we switch to an analytical estimation of the portion of orbit that was in sunlight // - we check against timewarp rate, instead of index, to avoid issues during timewarp blending if (v.mainBody.flightGlobalsIndex != 0 && TimeWarp.CurrentRate > 1000.0f) { sunlight = 1.0 - Sim.ShadowPeriod(v) / Sim.OrbitalPeriod(v); } // environment stuff atmo_factor = Sim.AtmosphereFactor(v.mainBody, position, sun_dir); gamma_transparency = Sim.GammaTransparency(v.mainBody, v.altitude); underwater = Sim.Underwater(v); breathable = Sim.Breathable(v, underwater); landed = Lib.Landed(v); // temperature at vessel position temperature = Sim.Temperature(v, position, sunlight, atmo_factor, out solar_flux, out albedo_flux, out body_flux, out total_flux); temp_diff = Sim.TempDiff(temperature, v.mainBody, landed); // radiation radiation = Radiation.Compute(v, position, gamma_transparency, sunlight, out blackout, out magnetosphere, out inner_belt, out outer_belt, out interstellar); // extended atmosphere thermosphere = Sim.InsideThermosphere(v); exosphere = Sim.InsideExosphere(v); // malfunction stuff malfunction = Reliability.HasMalfunction(v); critical = Reliability.HasCriticalFailure(v); // signal info antenna = new AntennaInfo(v); avoid_inf_recursion.Add(v.id); connection = Signal.connection(v, position, antenna, blackout, avoid_inf_recursion); transmitting = Science.transmitting(v, connection.linked); relaying = Signal.relaying(v, avoid_inf_recursion); avoid_inf_recursion.Remove(v.id); // habitat data volume = Habitat.tot_volume(v); surface = Habitat.tot_surface(v); pressure = Habitat.pressure(v); poisoning = Habitat.poisoning(v); shielding = Habitat.shielding(v); living_space = Habitat.living_space(v); comforts = new Comforts(v, landed, crew_count > 1, connection.linked); // data about greenhouses greenhouses = Greenhouse.Greenhouses(v); // other stuff gravioli = Sim.Graviolis(v); }
// return living space factor in a vessel public static double living_space(Vessel v) { // living space is the volume per-capita normalized against an 'ideal living space' and clamped in an acceptable range return Lib.Clamp((tot_volume(v) / Lib.CrewCount(v)) / Settings.IdealLivingSpace, 0.1, 1.0); }
// return quality-of-life bonus public static double Bonus(Vessel v, string k_name) { // get QoL data from db kerbal_data kd = DB.KerbalData(k_name); // calculate quality of life bonus return Bonus(kd.living_space, kd.entertainment, Lib.Landed(v), Signal.Link(v).linked, Lib.CrewCount(v) < 2u); }