public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Reliability reliability, double elapsed_s) { // if it has not malfunctioned if (Lib.Proto.GetUInt(m, "malfunctions") < 2) { // get epoch double epoch = Lib.Proto.GetDouble(m, "epoch"); // calculate epoch of failure if necessary if (epoch <= double.Epsilon) { double quality = Lib.Proto.GetDouble(m, "quality", 1.0); double start = Planetarium.GetUniversalTime(); epoch = start + reliability.mtbf * quality * 2.0 * Lib.RandomDouble(); Lib.Proto.Set(m, "start", start); Lib.Proto.Set(m, "epoch", epoch); } // if it has failed, trigger malfunction if (Planetarium.GetUniversalTime() > epoch) { Break(v, m); } } }
static void ProcessGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleGenerator generator, vessel_resources resources, double elapsed_s) { // play nice with BackgroundProcessing if (Kerbalism.detected_mods.BackgroundProcessing) { return; } // if active if (Lib.Proto.GetBool(m, "generatorIsActive")) { // get malfunction penalty double penalty = Reliability.Penalty(p, "Generator"); // create and commit recipe resource_recipe recipe = new resource_recipe(resource_recipe.converter_priority); foreach (var ir in generator.inputList) { recipe.Input(ir.name, ir.rate * elapsed_s); } foreach (var or in generator.outputList) { recipe.Output(or.name, or.rate * penalty * elapsed_s); } resources.Transform(recipe); } }
static void ProcessHarvester(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceHarvester harvester, vessel_resources resources, double elapsed_s) { // note: ignore stock temperature mechanic of harvesters // note: ignore autoshutdown // note: ignore depletion (stock seem to do the same) // note: using hard-coded crew bonus values from the wiki because the module data make zero sense (DERP ALERT) // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // if active if (Lib.Proto.GetBool(m, "IsActivated")) { // do nothing if full // note: comparing against previous amount if (resources.Info(v, harvester.ResourceName).level < harvester.FillAmount - double.Epsilon) { // get malfunction penalty double penalty = Reliability.Penalty(p, "Harvester"); // deduce crew bonus int exp_level = -1; if (harvester.UseSpecialistBonus) { foreach (ProtoCrewMember c in v.protoVessel.GetVesselCrew()) { exp_level = Math.Max(exp_level, c.trait == harvester.Specialty ? c.experienceLevel : -1); } } double exp_bonus = exp_level < 0 ? 1.0 : 5.0 + (double)exp_level * 4.0; // detect amount of ore in the ground AbundanceRequest request = new AbundanceRequest { Altitude = v.altitude, BodyId = v.mainBody.flightGlobalsIndex, CheckForLock = false, Latitude = v.latitude, Longitude = v.longitude, ResourceType = (HarvestTypes)harvester.HarvesterType, ResourceName = harvester.ResourceName }; double abundance = ResourceMap.Instance.GetAbundance(request); // if there is actually something (should be if active when unloaded) if (abundance > harvester.HarvestThreshold) { // create and commit recipe resource_recipe recipe = new resource_recipe(resource_recipe.harvester_priority); foreach (var ir in harvester.inputList) { recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s); } recipe.Output(harvester.ResourceName, abundance * harvester.Efficiency * exp_bonus * penalty * elapsed_s); resources.Transform(recipe); } } // undo stock behaviour by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
public ReliabilityInfo(ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Reliability module_prefab) { title = Lib.BuildString(p.partInfo.title, Lib.Color(" " + module_prefab.title, Lib.Kolor.LightGrey)); group = module_prefab.redundancy; broken = Lib.Proto.GetBool(m, "broken", false); critical = Lib.Proto.GetBool(m, "critical", false); partId = 0; need_maintenance = Lib.Proto.GetBool(m, "need_maintenance", false); bool quality = Lib.Proto.GetBool(m, "quality", false); if (module_prefab.rated_operation_duration > 0) { var operation_duration = Lib.Proto.GetDouble(m, "operation_duration", 0); rel_duration = operation_duration / Reliability.EffectiveDuration(quality, module_prefab.rated_operation_duration); rel_duration = Lib.Clamp(rel_duration, 0, 1); } if (module_prefab.rated_ignitions > 0) { var ignitions = Lib.Proto.GetInt(m, "ignitions", 0); rel_ignitions = (double)ignitions / Reliability.EffectiveDuration(quality, module_prefab.rated_ignitions); rel_ignitions = Lib.Clamp(rel_ignitions, 0, 1); } mtbf = Reliability.EffectiveMTBF(quality, module_prefab.mtbf); if (mtbf > 0) { var last_inspection = Lib.Proto.GetDouble(m, "last_inspection", 0); maintenance_after = last_inspection + mtbf * 0.5; } }
public ReliabilityInfo(Reliability module) { title = Lib.BuildString(module.part.partInfo.title, Lib.Color(" " + module.title, Lib.Kolor.LightGrey)); group = module.redundancy; broken = module.broken; critical = module.critical; partId = module.part.flightID; need_maintenance = module.needMaintenance; mtbf = Reliability.EffectiveMTBF(module.quality, module.mtbf); if (module.rated_operation_duration > 0) { rel_duration = module.operation_duration / Reliability.EffectiveDuration(module.quality, module.rated_operation_duration); rel_duration = Lib.Clamp(rel_duration, 0, 1); } if (module.rated_ignitions > 0) { rel_ignitions = (double)module.ignitions / Reliability.EffectiveIgnitions(module.quality, module.rated_ignitions); rel_ignitions = Lib.Clamp(rel_ignitions, 0, 1); } if (mtbf > 0) { maintenance_after = module.last_inspection + mtbf * 0.5; } }
// implement gravity ring mechanics for unloaded vessels public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, GravityRing ring, vessel_resources resources, double elapsed_s) { // get protomodule data float speed = Lib.Proto.GetFloat(m, "speed"); // get resource handler resource_info ec = resources.Info(vessel, "ElectricCharge"); // consume ec ec.Consume(ring.ec_rate * speed * elapsed_s * Reliability.Penalty(p, "GravityRing", 2.0)); // reset speed if there isn't enough ec // note: comparing against amount in previous simulation step if (ec.amount <= double.Epsilon) { speed = 0.0f; Lib.Proto.Set(m, "speed", speed); } // set entertainment // note: entertainmnent is only recomputed for loaded vessels, // so changing rate here does nothing until vessel is reloaded double rate = 1.0 + (ring.entertainment_rate - 1.0) * speed; Lib.Proto.Set(m, "rate", rate); }
public void Quality() { quality = !quality; // sync all other modules in the symmetry group foreach (Part p in part.symmetryCounterparts) { Reliability reliability = p.Modules[part.Modules.IndexOf(this)] as Reliability; if (reliability != null) { reliability.quality = !reliability.quality; } } // refresh VAB ui GameEvents.onEditorShipModified.Fire(EditorLogic.fetch.ship); }
public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Emitter emitter, resource_info ec, double elapsed_s) { // if there is enough EC // note: comparing against amount in previous simulation step if (ec.amount > double.Epsilon) { // get intensity double intensity = Lib.Proto.GetDouble(m, "intensity"); // consume EC ec.Consume(emitter.ec_rate * intensity * elapsed_s * Reliability.Penalty(p, "Emitter", 2.0)); } // else disable it else { Lib.Proto.Set(m, "intensity", 0.0); } }
// cause a part at random to malfunction public static void CauseMalfunction(Vessel v) { // if vessel is loaded if (v.loaded) { // choose a module at random var modules = v.FindPartModulesImplementing <Reliability>(); if (modules.Count == 0) { return; } var m = modules[Lib.RandomInt(modules.Count)]; // break it m.Break(); } // if vessel is not loaded else { // get all reliability modules var modules = new List <KeyValuePair <ProtoPartSnapshot, ProtoPartModuleSnapshot> >(); foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { foreach (ProtoPartModuleSnapshot m in p.modules) { if (m.moduleName == "Reliability") { modules.Add(new KeyValuePair <ProtoPartSnapshot, ProtoPartModuleSnapshot>(p, m)); } } } // choose one at random if (modules.Count == 0) { return; } var pair = modules[Lib.RandomInt(modules.Count)]; // break it Reliability.Break(v, pair.Value); } }
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(); }
GUIContent indicator_reliability(Vessel v, vessel_info vi) { GUIContent state = new GUIContent(); uint max_malfunctions = vi.max_malfunction; if (max_malfunctions == 0) { state.image = icon_malfunction_nominal; state.tooltip = "No malfunctions"; } else if (max_malfunctions == 1) { state.image = icon_malfunction_warning; state.tooltip = "Minor malfunctions"; } else { state.image = icon_malfunction_danger; state.tooltip = "Major malfunctions"; } if (vi.avg_quality > 0.0) state.tooltip += Lib.BuildString("\n<i>Quality: ", Reliability.QualityToString(vi.avg_quality), "</i>"); return state; }
static void ProcessPanel(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleDeployableSolarPanel panel, vessel_info info, resource_info ec, double elapsed_s) { // note: we ignore temperature curve, and make sure it is not relavant in the MM patch // note: we ignore power curve, that is used by no panel as far as I know // play nice with BackgroundProcessing if (Kerbalism.detected_mods.BackgroundProcessing) { return; } // if in sunlight and extended if (info.sunlight > double.Epsilon && m.moduleValues.GetValue("stateString") == "EXTENDED") { // get panel normal direction Vector3d normal = panel.part.FindModelComponent <Transform>(panel.raycastTransformName).forward; // calculate cosine factor // note: for gameplay reasons, we ignore tracking panel pivots // note: a possible optimization here is to cache the transform lookup (unity was coded by monkeys) double cosine_factor = panel.sunTracking ? 1.0 : Math.Max(Vector3d.Dot(info.sun_dir, (v.transform.rotation * p.rotation * normal).normalized), 0.0); // calculate normalized solar flux // note: this include fractional sunlight if integrated over orbit // note: this include atmospheric absorption if inside an atmosphere double norm_solar_flux = info.solar_flux / Sim.SolarFluxAtHome(); // calculate output double output = panel.chargeRate // nominal panel charge rate at 1 AU * norm_solar_flux // normalized flux at panel distance from sun * cosine_factor // cosine factor of panel orientation * Reliability.Penalty(p, "Panel"); // malfunctioned panel penalty // produce EC ec.Produce(output * elapsed_s); } }
static void Incentive_redundancy(Vessel v, string redundancy) { if (v.loaded) { foreach (Reliability m in Lib.FindModules <Reliability>(v)) { if (m.redundancy == redundancy) { m.next += m.next - m.last; } } } else { foreach (ProtoPartModuleSnapshot m in Lib.FindModules(v.protoVessel, "Reliability")) { // find part ProtoPartSnapshot p = v.protoVessel.protoPartSnapshots.Find(k => k.modules.Contains(m)); // find module prefab string type = Lib.Proto.GetString(m, "type", string.Empty); Reliability reliability = p.partPrefab.FindModulesImplementing <Reliability>().Find(k => k.type == type); if (reliability == null) { continue; } // double time to next failure if (reliability.redundancy == redundancy) { double last = Lib.Proto.GetDouble(m, "last"); double next = Lib.Proto.GetDouble(m, "next"); Lib.Proto.Set(m, "next", next + (next - last)); } } } }
// 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); } }
public static void update(Vessel v, vessel_info vi, VesselData vd, vessel_resources resources, double elapsed_s) { // get most used resource handlers resource_info ec = resources.Info(v, "ElectricCharge"); // store data required to support multiple modules of same type in a part var PD = new Dictionary <string, Lib.module_prefab_data>(); // for each part foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; // get all module prefabs var module_prefabs = part_prefab.FindModulesImplementing <PartModule>(); // clear module indexes PD.Clear(); // for each module foreach (ProtoPartModuleSnapshot m in p.modules) { // get module type // if the type is unknown, skip it module_type type = ModuleType(m.moduleName); if (type == module_type.Unknown) { continue; } // get the module prefab // if the prefab doesn't contain this module, skip it PartModule module_prefab = Lib.ModulePrefab(module_prefabs, m.moduleName, PD); if (!module_prefab) { continue; } // if the module is disabled, skip it // note: this must be done after ModulePrefab is called, so that indexes are right if (!Lib.Proto.GetBool(m, "isEnabled")) { continue; } // process modules // note: this should be a fast switch, possibly compiled to a jump table switch (type) { case module_type.Reliability: Reliability.BackgroundUpdate(v, p, m, module_prefab as Reliability); break; case module_type.Experiment: Experiment.BackgroundUpdate(v, m, module_prefab as Experiment, ec, elapsed_s); break; case module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, m, module_prefab as Greenhouse, vi, resources, elapsed_s); break; case module_type.GravityRing: GravityRing.BackgroundUpdate(v, p, m, module_prefab as GravityRing, ec, elapsed_s); break; case module_type.Emitter: Emitter.BackgroundUpdate(v, p, m, module_prefab as Emitter, ec, elapsed_s); break; case module_type.Harvester: Harvester.BackgroundUpdate(v, m, module_prefab as Harvester, elapsed_s); break; case module_type.Laboratory: Laboratory.BackgroundUpdate(v, p, m, module_prefab as Laboratory, ec, elapsed_s); break; case module_type.Command: ProcessCommand(v, p, m, module_prefab as ModuleCommand, resources, elapsed_s); break; case module_type.Panel: ProcessPanel(v, p, m, module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break; case module_type.Generator: ProcessGenerator(v, p, m, module_prefab as ModuleGenerator, resources, elapsed_s); break; case module_type.Converter: ProcessConverter(v, p, m, module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case module_type.Drill: ProcessHarvester(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; case module_type.AsteroidDrill: ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; case module_type.StockLab: ProcessStockLab(v, p, m, module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case module_type.Light: ProcessLight(v, p, m, module_prefab as ModuleLight, ec, elapsed_s); break; case module_type.Scanner: ProcessScanner(v, p, m, module_prefab, part_prefab, vd, ec, elapsed_s); break; case module_type.CurvedPanel: ProcessCurvedPanel(v, p, m, module_prefab, part_prefab, vi, ec, elapsed_s); break; case module_type.FissionGenerator: ProcessFissionGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case module_type.CryoTank: ProcessCryoTank(v, p, m, module_prefab, resources, elapsed_s); break; } } } }
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); }
// 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); } }
// implement greenhouse mechanics for unloaded vessels public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Greenhouse greenhouse, vessel_info info, vessel_resources resources, double elapsed_s) { // get protomodule data bool door_opened = Lib.Proto.GetBool(m, "door_opened"); double growth = Lib.Proto.GetDouble(m, "growth"); float lamps = Lib.Proto.GetFloat(m, "lamps"); double lighting = Lib.Proto.GetDouble(m, "lighting"); // if lamp is on if (lamps > float.Epsilon) { // get resource handler resource_info ec = resources.Info(vessel, "ElectricCharge"); // consume ec ec.Consume(greenhouse.ec_rate * lamps * elapsed_s * Reliability.Penalty(p, "Greenhouse", 2.0)); // shut down the light if there isn't enough ec // note: comparing against amount at previous simulation step if (ec.amount <= double.Epsilon) lamps = 0.0f; } // determine lighting conditions // note: we ignore sun direction for gameplay reasons: else the user must reorient the greenhouse as the planets dance over time // - natural light depend on: distance from sun, direct sunlight, door status // - artificial light depend on: lamps tweakable and ec available, door status lighting = info.solar_flux / Sim.SolarFluxAtHome() * (door_opened ? 1.0 : 0.0) + lamps; // if can use waste, and there is some lighting double waste_perc = 0.0; if (greenhouse.waste_name.Length > 0 && lighting > double.Epsilon) { // get resource handler resource_info waste = resources.Info(vessel, greenhouse.waste_name); // consume waste waste.Consume(greenhouse.waste_rate * elapsed_s); // determine waste bonus // note: comparing against amount from previous simulation step waste_perc = Math.Min(waste.amount / greenhouse.waste_rate, 1.0); } // determine growth bonus double growth_bonus = greenhouse.soil_bonus * (info.landed ? 1.0 : 0.0) + greenhouse.waste_bonus * waste_perc; // grow the crop double growing = greenhouse.growth_rate * (1.0 + growth_bonus) * lighting; growth += elapsed_s * growing; // if it is harvest time if (growth >= 1.0) { // reset growth growth = 0.0; // produce food resources.Produce(vessel, greenhouse.resource_name, greenhouse.harvest_size); // show a message to the user Message.Post(Lib.BuildString("On <color=FFFFFF>", vessel.vesselName, "</color> the crop harvest produced <color=FFFFFF>", greenhouse.harvest_size.ToString("F0"), " ", greenhouse.resource_name, "</color>")); // record first space harvest if (!info.landed && DB.Ready()) DB.Landmarks().space_harvest = 1; } // store data Lib.Proto.Set(m, "growth", growth); Lib.Proto.Set(m, "lamps", lamps); Lib.Proto.Set(m, "lighting", lighting); Lib.Proto.Set(m, "growth_diff", growing); }
public static void Update(Vessel v, VesselData vd, VesselResources resources, double elapsed_s) { if (!Lib.IsVessel(v)) { return; } // get most used resource handlers ResourceInfo ec = resources.GetResource(v, "ElectricCharge"); List <ResourceInfo> allResources = resources.GetAllResources(v); Dictionary <string, double> availableResources = new Dictionary <string, double>(); foreach (var ri in allResources) { availableResources[ri.ResourceName] = ri.Amount; } List <KeyValuePair <string, double> > resourceChangeRequests = new List <KeyValuePair <string, double> >(); foreach (var e in Background_PMs(v)) { switch (e.type) { case Module_type.Reliability: Reliability.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Reliability, elapsed_s); break; case Module_type.Experiment: (e.module_prefab as Experiment).BackgroundUpdate(v, vd, e.m, ec, resources, elapsed_s); break; // experiments use the prefab as a singleton instead of a static method case Module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, e.m, e.module_prefab as Greenhouse, vd, resources, elapsed_s); break; case Module_type.GravityRing: GravityRing.BackgroundUpdate(v, e.p, e.m, e.module_prefab as GravityRing, ec, elapsed_s); break; case Module_type.Harvester: Harvester.BackgroundUpdate(v, e.m, e.module_prefab as Harvester, elapsed_s); break; // Kerbalism ground and air harvester module case Module_type.Laboratory: Laboratory.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Laboratory, ec, elapsed_s); break; case Module_type.Command: ProcessCommand(v, e.p, e.m, e.module_prefab as ModuleCommand, resources, elapsed_s); break; case Module_type.Generator: ProcessGenerator(v, e.p, e.m, e.module_prefab as ModuleGenerator, resources, elapsed_s); break; case Module_type.Converter: ProcessConverter(v, e.p, e.m, e.module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case Module_type.Drill: ProcessDrill(v, e.p, e.m, e.module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; // Stock ground harvester module // case Module_type.AsteroidDrill: ProcessAsteroidDrill(v, e.p, e.m, e.module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; // Stock asteroid harvester module case Module_type.StockLab: ProcessStockLab(v, e.p, e.m, e.module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case Module_type.Light: ProcessLight(v, e.p, e.m, e.module_prefab as ModuleLight, ec, elapsed_s); break; case Module_type.Scanner: KerbalismScansat.BackgroundUpdate(v, e.p, e.m, e.module_prefab as KerbalismScansat, e.part_prefab, vd, ec, elapsed_s); break; case Module_type.FissionGenerator: ProcessFissionGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.CryoTank: ProcessCryoTank(v, e.p, e.m, e.module_prefab, resources, ec, elapsed_s); break; case Module_type.FNGenerator: ProcessFNGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.SolarPanelFixer: SolarPanelFixer.BackgroundUpdate(v, e.m, e.module_prefab as SolarPanelFixer, vd, ec, elapsed_s); break; case Module_type.KerbalismSentinel: KerbalismSentinel.BackgroundUpdate(v, e.m, e.module_prefab as KerbalismSentinel, vd, ec, elapsed_s); break; case Module_type.APIModule: ProcessApiModule(v, e.p, e.m, e.part_prefab, e.module_prefab, resources, availableResources, resourceChangeRequests, elapsed_s); break; } } }
static void ProcessConverter(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Part part_prefab, int index, vessel_resources resources, double elapsed_s) { // note: support multiple resource converters // note: ignore stock temperature mechanic of converters // note: ignore autoshutdown // note: using hard-coded crew bonus values from the wiki because the module data make zero sense (DERP ALERT) // note: non-mandatory resources 'dynamically scale the ratios', that is exactly what mandatory resources do too (DERP ALERT) // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation) // note: support PlanetaryBaseSystem converters // note: support NearFuture reactors // get converter var converter_prefabs = part_prefab.Modules.GetModules <ModuleResourceConverter>(); if (index >= converter_prefabs.Count) { return; } ModuleResourceConverter converter = converter_prefabs[index] as ModuleResourceConverter; // 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) { // get malfunction penalty double penalty = Reliability.Penalty(p, "Converter"); // deduce crew bonus int exp_level = -1; if (converter.UseSpecialistBonus) { foreach (ProtoCrewMember c in v.protoVessel.GetVesselCrew()) { exp_level = Math.Max(exp_level, c.trait == converter.Specialty ? c.experienceLevel : -1); } } double exp_bonus = exp_level < 0 ? 1.0 : 5.0 + (double)exp_level * 4.0; // create and commit recipe resource_recipe recipe = new resource_recipe(resource_recipe.converter_priority); foreach (var ir in converter.inputList) { recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s); } foreach (var or in converter.outputList) { recipe.Output(or.ResourceName, or.Ratio * penalty * exp_bonus * elapsed_s); } resources.Transform(recipe); } // undo stock behaviour by forcing last_update_time to now Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime()); } }
public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Reliability reliability) { // if it has not malfunctioned if (!Lib.Proto.GetBool(m, "broken")) { // get time of next failure double next = Lib.Proto.GetDouble(m, "next"); // get quality bool quality = Lib.Proto.GetBool(m, "quality"); // calculate epoch of failure if necessary if (next <= double.Epsilon) { double last = Planetarium.GetUniversalTime(); next = last + reliability.mtbf * (quality ? Settings.QualityScale : 1.0) * 2.0 * Lib.RandomDouble(); Lib.Proto.Set(m, "last", last); Lib.Proto.Set(m, "next", next); } // if it has failed, trigger malfunction if (Planetarium.GetUniversalTime() > next) { ProtoBreak(v, p, m); } } }
public static void Update(Vessel v, Vessel_info vi, VesselData vd, Vessel_resources resources, double elapsed_s) { if (!Lib.IsVessel(v)) { return; } // get most used resource handlers Resource_info ec = resources.Info(v, "ElectricCharge"); // This is basically handled in cache. However, when accelerating time warp while // the vessel is in shadow, the cache logic doesn't kick in soon enough. So we double-check here if (TimeWarp.CurrentRate > 1000.0f || elapsed_s > 150) // we're time warping fast... { vi.highspeedWarp(v); } foreach (var e in Background_PMs(v)) { switch (e.type) { case Module_type.Reliability: Reliability.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Reliability); break; case Module_type.Experiment: Experiment.BackgroundUpdate(v, e.m, e.module_prefab as Experiment, ec, resources, elapsed_s); break; case Module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, e.m, e.module_prefab as Greenhouse, vi, resources, elapsed_s); break; case Module_type.GravityRing: GravityRing.BackgroundUpdate(v, e.p, e.m, e.module_prefab as GravityRing, ec, elapsed_s); break; case Module_type.Emitter: Emitter.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Emitter, ec, elapsed_s); break; case Module_type.Harvester: Harvester.BackgroundUpdate(v, e.m, e.module_prefab as Harvester, elapsed_s); break; // Kerbalism ground and air harvester module case Module_type.Laboratory: Laboratory.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Laboratory, ec, elapsed_s); break; case Module_type.Command: ProcessCommand(v, e.p, e.m, e.module_prefab as ModuleCommand, resources, elapsed_s); break; case Module_type.Panel: ProcessPanel(v, e.p, e.m, e.module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break; case Module_type.Generator: ProcessGenerator(v, e.p, e.m, e.module_prefab as ModuleGenerator, resources, elapsed_s); break; case Module_type.Converter: ProcessConverter(v, e.p, e.m, e.module_prefab as ModuleResourceConverter, resources, elapsed_s); break; case Module_type.Drill: ProcessDrill(v, e.p, e.m, e.module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; // Stock ground harvester module case Module_type.AsteroidDrill: ProcessAsteroidDrill(v, e.p, e.m, e.module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; // Stock asteroid harvester module case Module_type.StockLab: ProcessStockLab(v, e.p, e.m, e.module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case Module_type.Light: ProcessLight(v, e.p, e.m, e.module_prefab as ModuleLight, ec, elapsed_s); break; case Module_type.Scanner: KerbalismScansat.BackgroundUpdate(v, e.p, e.m, e.module_prefab as KerbalismScansat, e.part_prefab, vd, ec, elapsed_s); break; case Module_type.CurvedPanel: ProcessCurvedPanel(v, e.p, e.m, e.module_prefab, e.part_prefab, vi, ec, elapsed_s); break; case Module_type.FissionGenerator: ProcessFissionGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.CryoTank: ProcessCryoTank(v, e.p, e.m, e.module_prefab, resources, ec, elapsed_s); break; case Module_type.FNGenerator: ProcessFNGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.NonRechargeBattery: ProcessNonRechargeBattery(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break; case Module_type.KerbalismProcess: KerbalismProcess.BackgroundUpdate(v, e.m, e.module_prefab as KerbalismProcess, ec, resources, elapsed_s); break; } } }
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); }
public static void update(Vessel v, vessel_info vi, vessel_data vd, vessel_resources resources, double elapsed_s) { // get most used resource handlers resource_info ec = resources.Info(v, "ElectricCharge"); // for each part foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots) { // a part can contain multiple resource converters int converter_index = 0; // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; // for each module foreach (ProtoPartModuleSnapshot m in p.modules) { // get the module prefab PartModule module_prefab = Lib.FindModule(part_prefab, m.moduleName); // if the prefab doesn't contain this module, skip it if (!module_prefab) { continue; } // process modules switch (m.moduleName) { case "Reliability": Reliability.BackgroundUpdate(v, m, module_prefab as Reliability, elapsed_s); break; case "Scrubber": Scrubber.BackgroundUpdate(v, m, module_prefab as Scrubber, vi, resources, elapsed_s); break; case "Recycler": Recycler.BackgroundUpdate(v, m, module_prefab as Recycler, resources, elapsed_s); break; case "Greenhouse": Greenhouse.BackgroundUpdate(v, p, m, module_prefab as Greenhouse, vi, resources, elapsed_s); break; case "GravityRing": GravityRing.BackgroundUpdate(v, p, m, module_prefab as GravityRing, resources, elapsed_s); break; case "Emitter": Emitter.BackgroundUpdate(v, p, m, module_prefab as Emitter, ec, elapsed_s); break; case "ModuleCommand": ProcessCommand(v, p, m, module_prefab as ModuleCommand, resources, elapsed_s); break; case "ModuleDeployableSolarPanel": ProcessPanel(v, p, m, module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s); break; case "ModuleGenerator": ProcessGenerator(v, p, m, module_prefab as ModuleGenerator, resources, elapsed_s); break; case "ModuleResourceConverter": case "ModuleKPBSConverter": case "FissionReactor": ProcessConverter(v, p, m, part_prefab, converter_index++, resources, elapsed_s); break; case "ModuleResourceHarvester": ProcessHarvester(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s); break; case "ModuleAsteroidDrill": ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break; case "ModuleScienceConverter": ProcessLab(v, p, m, module_prefab as ModuleScienceConverter, ec, elapsed_s); break; case "ModuleLight": case "ModuleColoredLensLight": case "ModuleMultiPointSurfaceLight": ProcessLight(v, p, m, module_prefab as ModuleLight, ec, elapsed_s); break; case "SCANsat": case "ModuleSCANresourceScanner": ProcessScanner(v, p, m, module_prefab, part_prefab, vd, ec, elapsed_s); break; case "ModuleCurvedSolarPanel": ProcessCurvedPanel(v, p, m, module_prefab, part_prefab, vi, ec, elapsed_s); break; case "FissionGenerator": ProcessFissionGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case "ModuleRadioisotopeGenerator": ProcessRadioisotopeGenerator(v, p, m, module_prefab, ec, elapsed_s); break; case "ModuleCryoTank": ProcessCryoTank(v, p, m, module_prefab, resources, elapsed_s); break; } } } }
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); }
public static List <ReliabilityInfo> BuildList(Vessel vessel) { var result = new List <ReliabilityInfo>(); if (vessel.loaded) { foreach (var r in Lib.FindModules <Reliability>(vessel)) { result.Add(new ReliabilityInfo(r)); } } else { var PD = new Dictionary <string, Lib.Module_prefab_data>(); foreach (ProtoPartSnapshot p in vessel.protoVessel.protoPartSnapshots) { // get part prefab (required for module properties) Part part_prefab = PartLoader.getPartInfoByName(p.partName).partPrefab; // get all module prefabs var module_prefabs = part_prefab.FindModulesImplementing <PartModule>(); // clear module indexes PD.Clear(); // for each module foreach (ProtoPartModuleSnapshot m in p.modules) { if (m.moduleName != "Reliability") { continue; } Reliability module_prefab = Lib.ModulePrefab(module_prefabs, m.moduleName, PD) as Reliability; if (!module_prefab) { continue; } // if the module is disabled, skip it // note: this must be done after ModulePrefab is called, so that indexes are right if (!Lib.Proto.GetBool(m, "isEnabled")) { continue; } result.Add(new ReliabilityInfo(p, m, module_prefab)); } } } result.Sort((a, b) => { if (a.group != b.group) { return(string.Compare(a.group, b.group, StringComparison.Ordinal)); } return(string.Compare(a.title, b.title)); }); return(result); }
public static void ProtoBreak(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m) { // get reliability module prefab string type = Lib.Proto.GetString(m, "type", string.Empty); Reliability reliability = p.partPrefab.FindModulesImplementing <Reliability>().Find(k => k.type == type); if (reliability == null) { return; } // if manned, or if safemode didn't trigger if (Cache.VesselInfo(v).crew_capacity > 0 || Lib.RandomDouble() > Settings.SafeModeChance) { // flag as broken Lib.Proto.Set(m, "broken", true); // determine if this is a critical failure bool critical = Lib.RandomDouble() < Settings.CriticalChance; Lib.Proto.Set(m, "critical", critical); // for each associated module foreach (var proto_module in p.modules.FindAll(k => k.moduleName == reliability.type)) { // disable the module Lib.Proto.Set(proto_module, "isEnabled", false); } // type-specific hacks switch (reliability.type) { case "ProcessController": foreach (ProcessController pc in p.partPrefab.FindModulesImplementing <ProcessController>()) { ProtoPartResourceSnapshot res = p.resources.Find(k => k.resourceName == pc.resource); if (res != null) { res.flowState = false; } } break; } // show message Broken_msg(v, reliability.title, critical); } // safe mode else { // reset age Lib.Proto.Set(m, "last", 0.0); Lib.Proto.Set(m, "next", 0.0); // notify user Safemode_msg(v, reliability.title); } // in any case, incentive redundancy if (Settings.IncentiveRedundancy) { Incentive_redundancy(v, reliability.redundancy); } }
public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Reliability reliability, double elapsed_s) { if (!PreferencesReliability.Instance.mtbfFailures) { return; } // check for existing malfunction and if it actually uses MTBF failures if (Lib.Proto.GetBool(m, "broken")) { return; } if (reliability.mtbf <= 0) { return; } // get time of next failure double next = Lib.Proto.GetDouble(m, "next"); bool quality = Lib.Proto.GetBool(m, "quality"); var now = Planetarium.GetUniversalTime(); // calculate epoch of failure if necessary if (next <= 0) { var guaranteed = reliability.mtbf / 2.0; var r = 1 - Math.Pow(Lib.RandomDouble(), 3); next = now + guaranteed + reliability.mtbf * (quality ? Settings.QualityScale : 1.0) * r; Lib.Proto.Set(m, "last", now); Lib.Proto.Set(m, "next", next); #if DEBUG_RELIABILITY Lib.Log("Reliability: background MTBF failure in " + (now - next) + " for " + p); #endif } var rad = v.KerbalismData().EnvRadiation; var decay = RadiationDecay(quality, rad, elapsed_s, reliability.rated_radiation, reliability.radiation_decay_rate); if (decay > 0) { next -= decay; Lib.Proto.Set(m, "next", next); } // if it has failed, trigger malfunction if (now > next) { #if DEBUG_RELIABILITY Lib.Log("Reliablity: background MTBF failure for " + p); #endif ProtoBreak(v, p, m); } }