예제 #1
0
        // execute the recipe
        public void Execute(Vessel v, vessel_resources resources)
        {
            // determine worst input ratio
            double worst_input = 1.0;

            foreach (var pair in inputs)
            {
                if (pair.Value > double.Epsilon) //< avoid division by zero
                {
                    resource_info res = resources.Info(v, pair.Key);
                    worst_input = Math.Min(worst_input, Math.Max(0.0, res.amount + res.deferred) / pair.Value);
                }
            }

            // consume inputs
            foreach (var pair in inputs)
            {
                resource_info res = resources.Info(v, pair.Key);
                res.Consume(pair.Value * worst_input);
            }

            // produce outputs
            foreach (var pair in outputs)
            {
                resource_info res = resources.Info(v, pair.Key);
                res.Produce(pair.Value * worst_input);
            }
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
            }
        }
예제 #5
0
        State venting()
        {
            // in flight
            if (Lib.IsFlight())
            {
                // shortcuts
                resource_info vessel_atmo = ResourceCache.Info(vessel, "Atmosphere");
                PartResource  hab_atmo    = part.Resources["Atmosphere"];

                // get amount of atmosphere in part
                double hab_amount = hab_atmo.amount;

                // venting succeeded if the amount reached zero
                if (hab_amount <= double.Epsilon)
                {
                    return(State.disabled);
                }

                // determine venting speed
                double amount = volume * equalize_speed * Kerbalism.elapsed_s;

                // consume from the part, clamp amount to what's available in the part
                amount           = Math.Min(amount, hab_atmo.amount);
                hab_atmo.amount -= amount;

                // produce in all enabled habs in the vessel
                // (attempt recovery, but dump overboard if there is no capacity left)
                vessel_atmo.Produce(amount);

                // venting still in progress
                return(State.venting);
            }
            // in the editors
            else
            {
                // set amount to zero
                part.Resources["Atmosphere"].amount = 0.0;

                // return new state
                return(State.disabled);
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        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
            // 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 dir 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;

                // 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

                // produce EC
                ec.Produce(output * elapsed_s);
            }
        }
예제 #8
0
        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);
            }
        }
예제 #9
0
        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);

                // 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;
        }
예제 #10
0
        State equalize()
        {
            // in flight
            if (Lib.IsFlight())
            {
                // shortcuts
                resource_info vessel_atmo = ResourceCache.Info(vessel, "Atmosphere");
                PartResource  hab_atmo    = part.Resources["Atmosphere"];

                // get level of atmosphere in vessel and part
                double vessel_level = vessel_atmo.level;
                double hab_level    = Lib.Level(part, "Atmosphere", true);

                // equalization succeeded if the levels are the same
                // note: this behave correctly in the case the hab is the only enabled one or not
                if (Math.Abs(vessel_level - hab_level) < 0.01)
                {
                    return(State.enabled);
                }

                // in case vessel pressure is dropping during equalization, it mean that pressure
                // control is not enough so we just enable the hab while not fully equalized
                if (vessel_atmo.rate < 0.0)
                {
                    return(State.enabled);
                }

                // determine equalization speed
                // we deal with the case where a big hab is sucking all atmosphere from the rest of the vessel
                double amount = Math.Min(Cache.VesselInfo(vessel).volume, volume) * equalize_speed * Kerbalism.elapsed_s;

                // vessel pressure is higher
                if (vessel_level > hab_level)
                {
                    // clamp amount to what's available in the vessel and what can fit in the part
                    amount = Math.Min(amount, vessel_atmo.amount);
                    amount = Math.Min(amount, hab_atmo.maxAmount - hab_atmo.amount);

                    // consume from all enabled habs in the vessel
                    vessel_atmo.Consume(amount);

                    // produce in the part
                    hab_atmo.amount += amount;
                }
                // vessel pressure is lower
                else
                {
                    // consume from the part, clamp amount to what's available in the part
                    amount           = Math.Min(amount, hab_atmo.amount);
                    hab_atmo.amount -= amount;

                    // produce in all enabled habs in the vessel
                    // (attempt recovery, but dump overboard if there is no capacity left)
                    vessel_atmo.Produce(amount);
                }

                // equalization still in progress
                return(State.equalizing);
            }
            // in the editors
            else
            {
                // set amount to max capacity
                PartResource hab_atmo = part.Resources["Atmosphere"];
                hab_atmo.amount = hab_atmo.maxAmount;

                // return new state
                return(State.enabled);
            }
        }