static void ProcessFissionGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule fission_generator, Resource_info ec, double elapsed_s) { // note: ignore heat double power = Lib.ReflectionValue <float>(fission_generator, "PowerGeneration"); var reactor = p.modules.Find(k => k.moduleName == "FissionReactor"); double tweakable = reactor == null ? 1.0 : Lib.ConfigValue(reactor.moduleValues, "CurrentPowerPercent", 100.0) * 0.01; ec.Produce(power * tweakable * elapsed_s); }
static void ProcessRadioisotopeGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule radioisotope_generator, Resource_info ec, double elapsed_s) { // note: doesn't support easy mode double power = Lib.ReflectionValue <float>(radioisotope_generator, "BasePower"); double half_life = Lib.ReflectionValue <float>(radioisotope_generator, "HalfLife"); double mission_time = v.missionTime / (3600.0 * Lib.HoursInDay() * Lib.DaysInYear()); double remaining = Math.Pow(2.0, (-mission_time) / half_life); ec.Produce(power * remaining * elapsed_s); }
private static void RunProcessTick(Vessel v, double elapsed_s, double ec_produced, List <KeyValuePair <string, double> > resourcesProduced, double ec_consumed, List <KeyValuePair <string, double> > resourcesConsumed, Resource_info ec, Vessel_resources resources) { // evaluate process rate double rate = 1; if (ec_consumed < ec.amount) { rate = ec.amount / ec_consumed; } foreach (var consumed in resourcesConsumed) { var ri = resources.Info(v, consumed.Key); rate = Math.Min(rate, Lib.Clamp(ri.amount / (consumed.Value * elapsed_s), 0, 1)); } foreach (var produced in resourcesProduced) { var ri = resources.Info(v, produced.Key); var capacityAvailable = ri.capacity - ri.amount; var amountProduced = produced.Value * elapsed_s; if (capacityAvailable < amountProduced) { rate = Math.Min(rate, Lib.Clamp(capacityAvailable / amountProduced, 0, 1)); } } // produce/consume according to rate if (rate < double.Epsilon) { return; } ec.Consume(ec_consumed * elapsed_s * rate, "module process"); ec.Produce(ec_produced * elapsed_s * rate, "module process"); foreach (var consumed in resourcesConsumed) { resources.Info(v, consumed.Key).Consume(consumed.Value * elapsed_s * rate, "module process"); } foreach (var produced in resourcesProduced) { resources.Info(v, produced.Key).Produce(produced.Value * elapsed_s * rate, "module process"); } }
static void ProcessCurvedPanel(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule curved_panel, Part part_prefab, Vessel_info info, Resource_info ec, double elapsed_s) { // note: we assume deployed, this is a current limitation // if in sunlight if (info.sunlight > double.Epsilon) { // get values from module string transform_name = Lib.ReflectionValue <string>(curved_panel, "PanelTransformName"); float tot_rate = Lib.ReflectionValue <float>(curved_panel, "TotalEnergyRate"); // get components Transform[] components = part_prefab.FindModelTransforms(transform_name); if (components.Length == 0) { return; } // 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 rate per component double rate = (double)tot_rate / (double)components.Length; // calculate world-space part rotation quaternion // note: a possible optimization here is to cache the transform lookup (unity was coded by monkeys) Quaternion rot = v.transform.rotation * p.rotation; // calculate output of all components double output = 0.0; foreach (Transform t in components) { output += rate // nominal rate per-component at 1 AU * norm_solar_flux // normalized solar flux at panel distance from sun * Math.Max(Vector3d.Dot(info.sun_dir, (rot * t.forward).normalized), 0.0); // cosine factor of component orientation } // produce EC ec.Produce(output * elapsed_s); } }
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 relevant in the MM patch // note: cylindrical and spherical panels are not supported // note: we assume the tracking target is SUN // if in sunlight and extended if (info.sunlight > double.Epsilon && m.moduleValues.GetValue("deployState") == "EXTENDED") { // get panel normal/pivot direction in world space Transform tr = panel.part.FindModelComponent <Transform>(panel.pivotName); Vector3d dir = panel.isTracking ? tr.up : tr.forward; dir = (v.transform.rotation * p.rotation * dir).normalized; float age = (float)(v.missionTime / (Lib.HoursInDay() * 3600)); float effic_factor = panel.timeEfficCurve != null?panel.timeEfficCurve.Evaluate(age) : 1.0f; // calculate cosine factor // - fixed panel: clamped cosine // - tracking panel, tracking pivot enabled: around the pivot // - tracking panel, tracking pivot disabled: assume perfect alignment double cosine_factor = !panel.isTracking ? Math.Max(Vector3d.Dot(info.sun_dir, dir), 0.0) : Settings.TrackingPivot ? Math.Cos(1.57079632679 - Math.Acos(Vector3d.Dot(info.sun_dir, dir))) : 1.0; // calculate normalized solar flux // - this include fractional sunlight if integrated over orbit // - this include atmospheric absorption if inside an atmosphere double norm_solar_flux = info.solar_flux / Sim.SolarFluxAtHome(); // calculate output double output = panel.resHandler.outputResources[0].rate // nominal panel charge rate at 1 AU * norm_solar_flux // normalized flux at panel distance from sun * cosine_factor // cosine factor of panel orientation * effic_factor; // produce EC ec.Produce(output * elapsed_s, "panel"); } }
static void ProcessFNGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule fission_generator, Resource_info ec, double elapsed_s) { string maxPowerStr = Lib.Proto.GetString(m, "MaxPowerStr"); double maxPower = 0; if (maxPowerStr.Contains("GW")) { maxPower = double.Parse(maxPowerStr.Replace(" GW", "")) * 1000000; } else if (maxPowerStr.Contains("MW")) { maxPower = double.Parse(maxPowerStr.Replace(" MW", "")) * 1000; } else { maxPower = double.Parse(maxPowerStr.Replace(" KW", "")); } ec.Produce(maxPower * elapsed_s); }
public void FixedUpdate() { // do nothing in editor if (Lib.IsEditor()) { return; } // do nothing if there isn't a solar panel if (panel == null) { return; } // get resource handler Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge"); // get vessel data from cache Vessel_info info = Cache.VesselInfo(vessel); // do nothing if vessel is invalid if (!info.is_valid) { return; } // detect if sunlight is evaluated analytically bool analytical_sunlight = info.sunlight > 0.0 && info.sunlight < 1.0; // detect occlusion from other vessel parts // - we are only interested when the sunlight evaluation is discrete var collider = panel.hit.collider; bool locally_occluded = !analytical_sunlight && collider != null && info.sunlight > 0.0; // if panel is enabled and extended, and if sun is not occluded, not even locally if (panel.isEnabled && panel.deployState == ModuleDeployablePart.DeployState.EXTENDED && info.sunlight > 0.0 && !locally_occluded) { // calculate cosine factor // - the stock module is already computing the tracking direction double cosine_factor = Math.Max(Vector3d.Dot(info.sun_dir, panel.trackingDotTransform.forward), 0.0); // calculate normalized solar flux // - this include fractional sunlight if integrated over orbit // - this include atmospheric absorption if inside an atmosphere double norm_solar_flux = info.solar_flux / Sim.SolarFluxAtHome(); // calculate output double output = rate // nominal panel charge rate at 1 AU * norm_solar_flux // normalized flux at panel distance from sun * cosine_factor; // cosine factor of panel orientation // produce EC ec.Produce(output * Kerbalism.elapsed_s, "panel"); // update ui field_visibility = info.sunlight * 100.0; field_atmosphere = info.atmo_factor * 100.0; field_exposure = cosine_factor * 100.0; field_output = output; Fields["field_visibility"].guiActive = analytical_sunlight; Fields["field_atmosphere"].guiActive = info.atmo_factor < 1.0; Fields["field_exposure"].guiActive = true; Fields["field_output"].guiActive = true; } // if panel is disabled, retracted, or in shadow else { // hide ui Fields["field_visibility"].guiActive = false; Fields["field_atmosphere"].guiActive = false; Fields["field_exposure"].guiActive = false; Fields["field_output"].guiActive = false; } // update status ui field_status = analytical_sunlight ? "<color=#ffff22>Integrated over the orbit</color>" : locally_occluded ? "<color=#ff2222>Occluded by vessel</color>" : info.sunlight < 1.0 ? "<color=#ff2222>Occluded by celestial body</color>" : string.Empty; Fields["field_status"].guiActive = field_status.Length > 0; }