Example #1
0
        // return temperature of a vessel
        public static double Temperature(Vessel v, Vector3d position, double sunlight, double atmo_factor, out double solar_flux, out double albedo_flux, out double body_flux, out double total_flux)
        {
            // get vessel body
            CelestialBody body = v.mainBody;

            // get solar radiation
            solar_flux = SolarFlux(SunDistance(position)) * sunlight * atmo_factor;

            // get albedo radiation
            albedo_flux = body.flightGlobalsIndex == 0 ? 0.0 : AlbedoFlux(body, position);

            // get cooling radiation from the body
            body_flux = body.flightGlobalsIndex == 0 ? 0.0 : BodyFlux(body, v.altitude);

            // calculate total flux
            total_flux = solar_flux + albedo_flux + body_flux + BackgroundFlux();

            // calculate temperature
            double temp = BlackBodyTemperature(total_flux);

            // if inside atmosphere
            if (body.atmosphere && v.altitude < body.atmosphereDepth)
            {
                // calculate atmospheric temperature
                double atmo_temp = v.loaded ? v.rootPart.skinTemperature : body.GetTemperature(v.altitude);

                // mix between our temperature and the stock atmospheric model
                temp = Lib.Mix(atmo_temp, temp, Lib.Clamp(v.altitude / body.atmosphereDepth, 0.0, 1.0));
            }

            // finally, return the temperature
            return(temp);
        }
Example #2
0
        // return temperature of a vessel
        public static double Temperature(Vessel v, Vector3d position, double solar_flux, out double albedo_flux, out double body_flux, out double total_flux)
        {
            // get vessel body
            CelestialBody body = v.mainBody;

            // get albedo radiation
            albedo_flux = Lib.IsSun(body) ? 0.0 : AlbedoFlux(body, position);

            // get cooling radiation from the body
            body_flux = Lib.IsSun(body) ? 0.0 : BodyFlux(body, v.altitude);

            // calculate total flux
            total_flux = solar_flux + albedo_flux + body_flux + BackgroundFlux();

            // calculate temperature
            double temp = BlackBodyTemperature(total_flux);

            // if inside atmosphere
            if (body.atmosphere && v.altitude < body.atmosphereDepth)
            {
                // get atmospheric temperature
                double atmo_temp = body.GetTemperature(v.altitude);

                // mix between our temperature and the stock atmospheric model
                temp = Lib.Mix(atmo_temp, temp, Lib.Clamp(v.altitude / body.atmosphereDepth, 0.0, 1.0));
            }

            // finally, return the temperature
            return(temp);
        }
Example #3
0
  // called every frame
  public void Update()
  {
    if (HighLogic.LoadedSceneIsEditor) return;

    // get info from cache
    vessel_info vi = Cache.VesselInfo(this.vessel);

    // do nothing if vessel is invalid
    if (!vi.is_valid) return;

    // set reading
    switch(type)
    {
      case "temperature": Status = Lib.HumanReadableTemp(vi.temperature); break;
      case "radiation": Status = vi.radiation > double.Epsilon ? Lib.HumanReadableRadiationRate(vi.radiation) : "nominal"; break;
      case "solar_flux": Status = Lib.HumanReadableFlux(vi.solar_flux); break;
      case "albedo_flux": Status = Lib.HumanReadableFlux(vi.albedo_flux); break;
      case "body_flux": Status = Lib.HumanReadableFlux(vi.body_flux); break;
    }

    // manage pin animation on geiger counter
    if (pinanim != null && type == "radiation")
    {
      pinanim["pinanim"].normalizedTime = Lib.Clamp(pinfc.Evaluate((float)(vi.radiation * 3600.0)), 0.0f, 1.0f);
      pinanim.Play();
    }
  }
Example #4
0
 // produce or consume a resource on a vessel, irregardless if loaded or not
 public static double RequestResource(Vessel vessel, string resource_name, double quantity)
 {
     if (vessel.loaded)
     {
         return(vessel.rootPart.RequestResource(resource_name, quantity, ResourceFlowMode.ALL_VESSEL_BALANCE));
     }
     else
     {
         double diff     = quantity;
         double amount   = 0.0;
         double capacity = 0.0;
         foreach (ProtoPartSnapshot part in vessel.protoVessel.protoPartSnapshots)
         {
             foreach (ProtoPartResourceSnapshot res in part.resources)
             {
                 if (res.resourceName == resource_name && Convert.ToBoolean(res.resourceValues.GetValue("flowState")))
                 {
                     amount   = Convert.ToDouble(res.resourceValues.GetValue("amount"));
                     capacity = Convert.ToDouble(res.resourceValues.GetValue("maxAmount"));
                     double new_amount = Lib.Clamp(amount - diff, 0.0, capacity);
                     res.resourceValues.SetValue("amount", new_amount.ToString());
                     diff -= amount - new_amount;
                     if (Math.Abs(diff) <= double.Epsilon)
                     {
                         return(quantity);
                     }
                 }
             }
         }
         return(quantity - diff);
     }
 }
Example #5
0
        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;
            }
        }
Example #6
0
        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;
            }
        }
Example #7
0
        // return the surface radiation for the body specified (used by body info panel)
        public static double ComputeSurface(CelestialBody b, double gamma_transparency)
        {
            // store stuff
            Space   gsm;
            Vector3 p;
            float   D;

            // transform to local space once
            Vector3d position = ScaledSpace.LocalToScaledSpace(b.position);

            // accumulate radiation
            double        radiation = 0.0;
            CelestialBody body      = b;

            while (body != null)
            {
                RadiationBody  rb = Info(body);
                RadiationModel mf = rb.model;
                if (mf.Has_field())
                {
                    // generate radii-normalized GSM space
                    gsm = Gsm_space(rb, true);

                    // move the poing in GSM space
                    p = gsm.Transform_in(position);

                    // accumulate radiation and determine pause/belt flags
                    if (mf.has_inner)
                    {
                        D          = mf.Inner_func(p);
                        radiation += Lib.Clamp(D / -0.0666f, 0.0f, 1.0f) * rb.radiation_inner;
                    }
                    if (mf.has_outer)
                    {
                        D          = mf.Outer_func(p);
                        radiation += Lib.Clamp(D / -0.0333f, 0.0f, 1.0f) * rb.radiation_outer;
                    }
                    if (mf.has_pause)
                    {
                        gsm        = Gsm_space(rb, false);
                        p          = gsm.Transform_in(position);
                        D          = mf.Pause_func(p);
                        radiation += Lib.Clamp(D / -0.1332f, 0.0f, 1.0f) * rb.radiation_pause;
                    }
                }

                // avoid loops in the chain
                body = (body.referenceBody != null && body.referenceBody.referenceBody == body) ? null : body.referenceBody;
            }

            // add extern radiation
            radiation += PreferencesStorm.Instance.ExternRadiation;

            // clamp radiation to positive range
            // note: we avoid radiation going to zero by using a small positive value
            radiation = Math.Max(radiation, Nominal);

            // return radiation, scaled by gamma transparency if inside atmosphere
            return(radiation * gamma_transparency);
        }
        // execute the recipe
        public bool Execute(Vessel v, Vessel_Resources resources)
        {
            // determine worst input ratio
            // - pure input recipes can just underflow
            double worst_input = left;

            if (outputs.Count > 0)
            {
                for (int i = 0; i < inputs.Count; ++i)
                {
                    Entry         e   = inputs[i];
                    Resource_Info res = resources.Info(v, e.name);
                    worst_input = Lib.Clamp((res.amount + res.deferred) * e.inv_quantity, 0.0, worst_input);
                }
            }

            // determine worst output ratio
            // - pure output recipes can just overflow
            double worst_output = left;

            if (inputs.Count > 0)
            {
                for (int i = 0; i < outputs.Count; ++i)
                {
                    Entry e = outputs[i];
                    if (!e.dump) // ignore outputs that can dump overboard
                    {
                        Resource_Info res = resources.Info(v, e.name);
                        worst_output = Lib.Clamp((res.capacity - (res.amount + res.deferred)) * e.inv_quantity, 0.0, worst_output);
                    }
                }
            }

            // determine worst-io
            double worst_io = Math.Min(worst_input, worst_output);

            // consume inputs
            for (int i = 0; i < inputs.Count; ++i)
            {
                Entry e = inputs[i];
                resources.Consume(v, e.name, e.quantity * worst_io);
            }

            // produce outputs
            for (int i = 0; i < outputs.Count; ++i)
            {
                Entry e = outputs[i];
                resources.Produce(v, e.name, e.quantity * worst_io);
            }

            // update amount left to execute
            left -= worst_io;

            // the recipe was executed, at least partially
            return(worst_io > double.Epsilon);
        }
        public float Outer_func(Vector3 p)
        {
            p.x *= p.x < 0.0f ? outer_extension : outer_compression;
            float q  = Mathf.Sqrt(p.x * p.x + p.z * p.z) - outer_dist;
            float k  = Lib.Mix(outer_border_start, outer_border_end, Lib.Clamp(q / outer_dist, 0.0f, 1.0f));
            float d1 = Mathf.Sqrt(q * q + p.y * p.y) - outer_radius;
            float d2 = d1 + k;

            return(Mathf.Max(d1, -d2) + (outer_deform > 0.001 ? (Mathf.Sin(p.x * 5.0f) * Mathf.Sin(p.y * 7.0f) * Mathf.Sin(p.z * 6.0f)) * outer_deform : 0.0f));
        }
Example #10
0
        public static double AdjustedRate(Harvester harvester, CrewSpecs engineer_cs, List <ProtoCrewMember> crew, double abundance)
        {
            // Bonus(..., -2): a level 0 engineer will alreaday add 2 bonus points jsut because he's there,
            // regardless of level. efficiency will raise further with higher levels.
            int    bonus     = engineer_cs.Bonus(crew, -2);
            double crew_gain = 1 + bonus * Settings.HarvesterCrewLevelBonus;

            crew_gain = Lib.Clamp(crew_gain, 1, Settings.MaxHarvesterBonus);

            return(harvester.rate * crew_gain * (abundance / harvester.abundance_rate));
        }
Example #11
0
        // return cosmic radiation hitting the vessel, in rad/s
        public static double CosmicRadiation(Vessel v)
        {
            if (v.mainBody.flightGlobalsIndex == 0)
            {
                return(Settings.CosmicRadiation);
            }
            double magn_altitude = instance.bodies[v.mainBody.flightGlobalsIndex].magn_altitude;
            double magn_k        = magn_altitude > double.Epsilon ? Lib.Clamp((v.altitude - magn_altitude) / (magn_altitude * Settings.MagnetosphereFalloff), 0.0, 1.0) : 1.0;

            return(Settings.CosmicRadiation * magn_k);
        }
Example #12
0
        private static double Rate(Vessel v, double chunkSize, double maxCapacity, double elapsed, Resource_info ec, double ec_rate, Vessel_resources resources, List <KeyValuePair <string, double> > resourceDefs)
        {
            double result = Lib.Clamp(maxCapacity / chunkSize, 0, 1);

            result = Math.Min(result, Lib.Clamp(ec.amount / (ec_rate * elapsed), 0, 1));

            foreach (var p in resourceDefs)
            {
                var ri = resources.Info(v, p.Key);
                result = Math.Min(result, Lib.Clamp(ri.amount / (p.Value * elapsed), 0, 1));
            }

            return(result);
        }
Example #13
0
        private static void RunProcessTick(Vessel v, double elapsed_s,
                                           double ec_produced, List <KeyValuePair <string, double> > resourcesProduced,
                                           double ec_consumed, List <KeyValuePair <string, double> > resourcesConsumed,
                                           Resource_info ec, Vessel_resources resources)
        {
            // evaluate process rate
            double rate = 1;

            if (ec_consumed < ec.amount)
            {
                rate = ec.amount / ec_consumed;
            }

            foreach (var consumed in resourcesConsumed)
            {
                var ri = resources.Info(v, consumed.Key);
                rate = Math.Min(rate, Lib.Clamp(ri.amount / (consumed.Value * elapsed_s), 0, 1));
            }

            foreach (var produced in resourcesProduced)
            {
                var ri = resources.Info(v, produced.Key);
                var capacityAvailable = ri.capacity - ri.amount;
                var amountProduced    = produced.Value * elapsed_s;
                if (capacityAvailable < amountProduced)
                {
                    rate = Math.Min(rate, Lib.Clamp(capacityAvailable / amountProduced, 0, 1));
                }
            }

            // produce/consume according to rate
            if (rate < double.Epsilon)
            {
                return;
            }

            ec.Consume(ec_consumed * elapsed_s * rate, "module process");
            ec.Produce(ec_produced * elapsed_s * rate, "module process");

            foreach (var consumed in resourcesConsumed)
            {
                resources.Info(v, consumed.Key).Consume(consumed.Value * elapsed_s * rate, "module process");
            }
            foreach (var produced in resourcesProduced)
            {
                resources.Info(v, produced.Key).Produce(produced.Value * elapsed_s * rate, "module process");
            }
        }
Example #14
0
        /// <summary> Return a number [-0.15 .. 1.05] that represents current solar activity, clamped to [0 .. 1] if clamp is true </summary>
        public double SolarActivity(bool clamp = true)
        {
            if (solar_cycle <= 0)
            {
                return(0);
            }

            var t = (solar_cycle_offset + Planetarium.GetUniversalTime()) / solar_cycle * 2 * Math.PI; // Math.Sin/Cos works with radians

            // this gives a pseudo-erratic curve, see https://www.desmos.com/calculator/q5flvzvxia
            // in range -0.15 .. 1.05
            var r = (-Math.Cos(t) + Math.Sin(t * 75) / 5 + 0.9) / 2.0;

            if (clamp)
            {
                r = Lib.Clamp(r, 0.0, 1.0);
            }
            return(r);
        }
Example #15
0
        public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Laboratory lab, Resource_info ec, double elapsed_s)
        {
            // if enabled
            if (Lib.Proto.GetBool(m, "running"))
            {
                // if a researcher is not required, or the researcher is present
                background_researcher_cs = new CrewSpecs(lab.researcher);
                if (!background_researcher_cs || background_researcher_cs.Check(p.protoModuleCrew))
                {
                    double rate = lab.analysis_rate;
                    if (background_researcher_cs)
                    {
                        int    bonus     = background_researcher_cs.Bonus(p.protoModuleCrew);
                        double crew_gain = 1 + bonus * Settings.LaboratoryCrewLevelBonus;
                        crew_gain = Lib.Clamp(crew_gain, 1, Settings.MaxLaborartoryBonus);
                        rate     *= crew_gain;
                    }

                    // get sample to analyze
                    background_sample = NextSample(v);

                    // if there is a sample to analyze
                    if (background_sample != null)
                    {
                        // consume EC
                        ec.Consume(lab.ec_rate * elapsed_s);

                        // if there was ec
                        // - comparing against amount in previous simulation step
                        if (ec.amount > double.Epsilon)
                        {
                            // analyze the sample
                            var status = Analyze(v, background_sample, rate * elapsed_s);
                            if (status != Status.RUNNING)
                            {
                                Lib.Proto.Set(m, "running", false);
                            }
                        }
                    }
                }
            }
        }
Example #16
0
  public void Inspect()
  {
    // do nothing if something is wrong, or the eva kerbal is dead
    Vessel v = FlightGlobals.ActiveVessel;
    if (v == null || !v.isEVA || EVA.IsDead(v)) return;

    // if the kerbal isn't an engineer, show a message and do nothing
    if (v.GetVesselCrew()[0].trait != "Engineer")
    {
      Message.Post("Only <b>Engineers</b> can inspect parts");
      return;
    }

    // evaluate at what point we are in the lifetime
    double time_k = Lib.Clamp((age - min_lifetime) / max_lifetime, 0.0, 1.0);
    if (time_k > 0.75) Message.Post("This is going to fail");
    else if (time_k > 0.5) Message.Post("This is reaching its limit");
    else if (time_k > 0.25) Message.Post("This is still okay");
    else Message.Post("This is in good shape");
  }
Example #17
0
		private static void ResourceUpdate(Vessel v, Harvester harvester, double min_abundance, double elapsed_s)
		{
			double abundance = SampleAbundance(v, harvester);
			if (abundance > min_abundance)
			{
				double rate = harvester.rate;

				// Bonus(..., -2): a level 0 engineer will alreaday add 2 bonus points jsut because he's there,
				// regardless of level. efficiency will raise further with higher levels.
				int bonus = engineer_cs.Bonus(v, -2);
				double crew_gain = 1 + bonus * Settings.HarvesterCrewLevelBonus;
				crew_gain = Lib.Clamp(crew_gain, 1, Settings.MaxHarvesterBonus);
				rate *= crew_gain;

				Resource_recipe recipe = new Resource_recipe(harvester.part);
				recipe.Input("ElectricCharge", harvester.ec_rate * elapsed_s);
				recipe.Output(harvester.resource, harvester.rate * (abundance/harvester.abundance_rate) * elapsed_s, false);
				ResourceCache.Transform(v, recipe);
			}
		}
Example #18
0
        public override void SetDifficultyPreset(GameParameters.Preset preset)
        {
            switch (preset)
            {
            case GameParameters.Preset.Easy:
                lifetime            = false;
                stormFrequency      = Settings.StormFrequency * 0.9f;
                stormRadiation      = Settings.StormRadiation * 0.9f;
                shieldingEfficiency = Lib.Clamp(Settings.ShieldingEfficiency * 1.1f, 0.0f, 0.99f);
                break;

            case GameParameters.Preset.Normal:
                lifetime            = false;
                stormFrequency      = Settings.StormFrequency;
                stormRadiation      = Settings.StormRadiation;
                shieldingEfficiency = Lib.Clamp(Settings.ShieldingEfficiency, 0.0f, 0.99f);
                break;

            case GameParameters.Preset.Moderate:
                lifetime            = false;
                stormFrequency      = Settings.StormFrequency * 1.3f;
                stormRadiation      = Settings.StormRadiation * 1.2f;
                shieldingEfficiency = Lib.Clamp(Settings.ShieldingEfficiency * 0.9f, 0.0f, 0.99f);
                break;

            case GameParameters.Preset.Hard:
                lifetime            = true;
                stormFrequency      = Settings.StormFrequency * 1.5f;
                stormRadiation      = Settings.StormRadiation * 1.5f;
                shieldingEfficiency = Lib.Clamp(Settings.ShieldingEfficiency * 0.8f, 0.0f, 0.99f);
                break;

            default:
                break;
            }
        }
Example #19
0
 // 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));
 }
Example #20
0
        public void Execute(Vessel v, Vessel_info vi, Vessel_resources resources, double elapsed_s)
        {
            // store list of crew to kill
            List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>();

            // get input resource handler
            Resource_info res = input.Length > 0 ? resources.Info(v, input) : null;

            // determine message variant
            uint variant = vi.temperature < PreferencesLifeSupport.Instance.survivalTemperature ? 0 : 1u;

            // get product of all environment modifiers
            double k = Modifiers.Evaluate(v, vi, resources, modifiers);

            bool lifetime_enabled = PreferencesBasic.Instance.lifetime;

            // for each crew
            foreach (ProtoCrewMember c in Lib.CrewList(v))
            {
                // get kerbal data
                KerbalData kd = DB.Kerbal(c.name);

                // skip rescue kerbals
                if (kd.rescue)
                {
                    continue;
                }

                // skip disabled kerbals
                if (kd.disabled)
                {
                    continue;
                }

                // get kerbal property data from db
                RuleData rd = kd.Rule(name);
                rd.lifetime = lifetime_enabled && lifetime;

                // if continuous
                double step;
                if (interval <= double.Epsilon)
                {
                    // influence consumption by elapsed time
                    step = elapsed_s;
                }
                // if interval-based
                else
                {
                    // accumulate time
                    rd.time_since += elapsed_s;

                    // determine number of steps
                    step = Math.Floor(rd.time_since / interval);

                    // consume time
                    rd.time_since -= step * interval;

                    // remember if a meal is consumed/produced in this simulation step
                    if (step > 0.99)
                    {
                        res.SetMealHappened();
                    }
                    if (output.Length > 0 && step > 0.99)
                    {
                        ResourceCache.Info(v, output).SetMealHappened();
                    }
                }

                // if continuous, or if one or more intervals elapsed
                if (step > double.Epsilon)
                {
                    double r = rate * Variance(name, c, individuality);                      // kerbal-specific variance

                    // if there is a resource specified
                    if (res != null && r > double.Epsilon)
                    {
                        // determine amount of resource to consume
                        double required = r                                   // consumption rate
                                          * k                                 // product of environment modifiers
                                          * step;                             // seconds elapsed or number of steps

                        // if there is no output
                        if (output.Length == 0)
                        {
                            // simply consume (that is faster)
                            res.Consume(required);
                        }
                        // if there is an output and monitor is false
                        else if (!monitor)
                        {
                            // transform input into output resource
                            // - rules always dump excess overboard (because it is waste)
                            Resource_recipe recipe = new Resource_recipe((Part)null);                              // kerbals are not associated with a part
                            recipe.Input(input, required);
                            recipe.Output(output, required * ratio, true);
                            resources.Transform(recipe);
                        }
                        // if monitor then do not consume input resource and only produce output if resource percentage + monitor_offset is < 100%
                        else if ((res.amount / res.capacity) + monitor_offset < 1.0)
                        {
                            // simply produce (that is faster)
                            resources.Produce(v, output, required * ratio);
                        }
                    }

                    // degenerate:
                    // - if the environment modifier is not telling to reset (by being zero)
                    // - if the input threshold is reached if used
                    // - if this rule is resource-less, or if there was not enough resource in the vessel
                    if (input_threshold >= double.Epsilon)
                    {
                        if (res.amount >= double.Epsilon && res.capacity >= double.Epsilon)
                        {
                            trigger = (res.amount / res.capacity) + monitor_offset >= input_threshold;
                        }
                        else
                        {
                            trigger = false;
                        }
                    }
                    else
                    {
                        trigger = input.Length == 0 || res.amount <= double.Epsilon;
                    }

                    if (k > 0.0 && trigger)
                    {
                        rd.problem += degeneration                                   // degeneration rate per-second or per-interval
                                      * k                                            // product of environment modifiers
                                      * step                                         // seconds elapsed or by number of steps
                                      * Variance(name, c, variance);                 // kerbal-specific variance
                    }
                    // else slowly recover
                    else
                    {
                        rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002);
                    }
                }

                bool do_breakdown = false;

                if (breakdown && PreferencesBasic.Instance.stressBreakdowns)
                {
                    // stress level
                    double breakdown_probability = rd.problem / warning_threshold;
                    breakdown_probability = Lib.Clamp(breakdown_probability, 0.0, 1.0);

                    // use the stupidity of a kerbal.
                    // however, nobody is perfect - not even a kerbal with a stupidity of 0.
                    breakdown_probability *= c.stupidity * 0.6 + 0.4;

                    // apply the weekly error rate
                    breakdown_probability *= PreferencesBasic.Instance.stressBreakdownRate;

                    // now we have the probability for one failure per week, based on the
                    // individual stupidity and stress level of the kerbal.

                    breakdown_probability = (breakdown_probability * elapsed_s) / (Lib.DaysInYear() * Lib.HoursInDay() * 3600);
                    if (breakdown_probability > Lib.RandomDouble())
                    {
                        do_breakdown = true;

                        // we're stressed out and just made a major mistake, this further increases the stress level...
                        rd.problem += warning_threshold * 0.05;                         // add 5% of the warning treshold to current stress level
                    }
                }

                // kill kerbal if necessary
                if (rd.problem >= fatal_threshold)
                {
                    if (fatal_message.Length > 0)
                    {
                        Message.Post(breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(fatal_message, v, c, variant));
                    }

                    if (breakdown)
                    {
                        do_breakdown = true;

                        // move back between warning and danger level
                        rd.problem = (warning_threshold + danger_threshold) * 0.5;

                        // make sure next danger message is shown
                        rd.message = 1;
                    }
                    else
                    {
                        deferred_kills.Add(c);
                    }
                }
                // show messages
                else if (rd.problem >= danger_threshold && rd.message < 2)
                {
                    if (danger_message.Length > 0)
                    {
                        Message.Post(Severity.danger, Lib.ExpandMsg(danger_message, v, c, variant));
                    }
                    rd.message = 2;
                }
                else if (rd.problem >= warning_threshold && rd.message < 1)
                {
                    if (warning_message.Length > 0)
                    {
                        Message.Post(Severity.warning, Lib.ExpandMsg(warning_message, v, c, variant));
                    }
                    rd.message = 1;
                }
                else if (rd.problem < warning_threshold && rd.message > 0)
                {
                    if (relax_message.Length > 0)
                    {
                        Message.Post(Severity.relax, Lib.ExpandMsg(relax_message, v, c, variant));
                    }
                    rd.message = 0;
                }

                if (do_breakdown)
                {
                    // trigger breakdown event
                    Misc.Breakdown(v, c);
                }
            }

            // execute the deferred kills
            foreach (ProtoCrewMember c in deferred_kills)
            {
                Misc.Kill(v, c);
            }
        }
Example #21
0
 public void save(int integer)
 {
     integer = Lib.Clamp(integer + 32, 32, 255);
     sb.Append((char)integer);
 }
Example #22
0
 public override void ctrl(double value)
 {
   ring.speed = (float)Lib.Clamp(value, 0.0, 1.0);
   if (!ring.opened) ring.Open();
 }
Example #23
0
        // return the total environent radiation at position specified
        public static double Compute(Vessel v, Vector3d position, double gamma_transparency, double sunlight, out bool blackout,
                                     out bool magnetosphere, out bool inner_belt, out bool outer_belt, out bool interstellar)
        {
            // prepare out parameters
            blackout      = false;
            magnetosphere = false;
            inner_belt    = false;
            outer_belt    = false;
            interstellar  = false;

            // no-op when Radiation is disabled
            if (!Features.Radiation)
            {
                return(0.0);
            }

            // store stuff
            Space   gsm;
            Vector3 p;
            float   D;

            // transform to local space once
            position = ScaledSpace.LocalToScaledSpace(position);

            // accumulate radiation
            double        radiation = 0.0;
            CelestialBody body      = v.mainBody;

            while (body != null)
            {
                RadiationBody  rb = Info(body);
                RadiationModel mf = rb.model;
                if (mf.Has_field())
                {
                    // generate radii-normalized GSM space
                    gsm = Gsm_space(rb.body, FlightGlobals.Bodies[rb.reference]);

                    // move the poing in GSM space
                    p = gsm.Transform_in(position);

                    // accumulate radiation and determine pause/belt flags
                    if (mf.has_inner)
                    {
                        D           = mf.Inner_func(p);
                        radiation  += Lib.Clamp(D / -0.0666f, 0.0f, 1.0f) * rb.radiation_inner;
                        inner_belt |= D < 0.0f;
                    }
                    if (mf.has_outer)
                    {
                        D           = mf.Outer_func(p);
                        radiation  += Lib.Clamp(D / -0.0333f, 0.0f, 1.0f) * rb.radiation_outer;
                        outer_belt |= D < 0.0f;
                    }
                    if (mf.has_pause)
                    {
                        D              = mf.Pause_func(p);
                        radiation     += Lib.Clamp(D / -0.1332f, 0.0f, 1.0f) * rb.radiation_pause;
                        magnetosphere |= D < 0.0f && rb.body.flightGlobalsIndex != 0;                        //< ignore heliopause
                        interstellar  |= D > 0.0f && rb.body.flightGlobalsIndex == 0;                        //< outside heliopause
                    }
                }

                // avoid loops in the chain
                body = (body.referenceBody != null && body.referenceBody.referenceBody == body) ? null : body.referenceBody;
            }

            // add extern radiation
            radiation += PreferencesStorm.Instance.ExternRadiation;

            // add emitter radiation
            radiation += Emitter.Total(v);

            // if there is a storm in progress
            if (Storm.InProgress(v))
            {
                // inside a magnetopause (except heliosphere), blackout the signal
                // outside, add storm radiations modulated by sun visibility
                if (magnetosphere)
                {
                    blackout = true;
                }
                else
                {
                    radiation += PreferencesStorm.Instance.StormRadiation * sunlight;
                }
            }

            // clamp radiation to positive range
            // note: we avoid radiation going to zero by using a small positive value
            radiation = Math.Max(radiation, Nominal);

            // return radiation, scaled by gamma transparency if inside atmosphere
            return(radiation * gamma_transparency);
        }
Example #24
0
        public Comforts(Vessel v, bool env_firm_ground, bool env_not_alone, bool env_call_home)
        {
            // environment factors
            firm_ground = env_firm_ground;
            not_alone   = env_not_alone;
            call_home   = env_call_home;

            // if loaded
            if (v.loaded)
            {
                // scan parts for comfort
                foreach (Comfort c in Lib.FindModules <Comfort>(v))
                {
                    switch (c.bonus)
                    {
                    case "firm-ground": firm_ground = true; break;

                    case "not-alone": not_alone = true; break;

                    case "call-home": call_home = true; break;

                    case "exercise": exercise = true; break;

                    case "panorama": panorama = true; break;

                    case "plants": plants = true; break;
                    }
                }

                // scan parts for gravity ring
                if (ResourceCache.Info(v, "ElectricCharge").amount >= 0.01)
                {
                    firm_ground |= Lib.HasModule <GravityRing>(v, k => k.deployed);
                }
            }
            // if not loaded
            else
            {
                // scan parts for comfort
                foreach (ProtoPartModuleSnapshot m in Lib.FindModules(v.protoVessel, "Comfort"))
                {
                    switch (Lib.Proto.GetString(m, "bonus"))
                    {
                    case "firm-ground": firm_ground = true; break;

                    case "not-alone": not_alone = true; break;

                    case "call-home": call_home = true; break;

                    case "exercise": exercise = true; break;

                    case "panorama": panorama = true; break;

                    case "plants": plants = true; break;
                    }
                }

                // scan parts for gravity ring
                if (ResourceCache.Info(v, "ElectricCharge").amount >= 0.01)
                {
                    firm_ground |= Lib.HasModule(v.protoVessel, "GravityRing", k => Lib.Proto.GetBool(k, "deployed"));
                }
            }

            // calculate factor
            factor = 0.1;
            if (firm_ground)
            {
                factor += PreferencesComfort.Instance.firmGround;
            }
            if (not_alone)
            {
                factor += PreferencesComfort.Instance.notAlone;
            }
            if (call_home)
            {
                factor += PreferencesComfort.Instance.callHome;
            }
            if (exercise)
            {
                factor += PreferencesComfort.Instance.exercise;
            }
            if (panorama)
            {
                factor += PreferencesComfort.Instance.panorama;
            }
            if (plants)
            {
                factor += PreferencesComfort.Instance.plants;
            }
            factor = Lib.Clamp(factor, 0.1, 1.0);
        }
Example #25
0
        public Comforts(List <Part> parts, bool env_firm_ground, bool env_not_alone, bool env_call_home)
        {
            // environment factors
            firm_ground = env_firm_ground;
            not_alone   = env_not_alone;
            call_home   = env_call_home;

            // for each parts
            foreach (Part p in parts)
            {
                // for each modules in part
                foreach (PartModule m in p.Modules)
                {
                    // skip disabled modules
                    if (!m.isEnabled)
                    {
                        continue;
                    }

                    // comfort
                    if (m.moduleName == "Comfort")
                    {
                        Comfort c = m as Comfort;
                        switch (c.bonus)
                        {
                        case "firm-ground": firm_ground = true; break;

                        case "not-alone": not_alone = true; break;

                        case "call-home": call_home = true; break;

                        case "exercise": exercise = true; break;

                        case "panorama": panorama = true; break;

                        case "plants": plants = true; break;
                        }
                    }
                    // gravity ring
                    // - ignoring if ec is present or not here
                    else if (m.moduleName == "GravityRing")
                    {
                        GravityRing ring = m as GravityRing;
                        firm_ground |= ring.deployed;
                    }
                }
            }

            // calculate factor
            factor = 0.1;
            if (firm_ground)
            {
                factor += PreferencesComfort.Instance.firmGround;
            }
            if (not_alone)
            {
                factor += PreferencesComfort.Instance.notAlone;
            }
            if (call_home)
            {
                factor += PreferencesComfort.Instance.callHome;
            }
            if (exercise)
            {
                factor += PreferencesComfort.Instance.exercise;
            }
            if (panorama)
            {
                factor += PreferencesComfort.Instance.panorama;
            }
            factor = Lib.Clamp(factor, 0.1, 1.0);
        }
Example #26
0
        public void Execute(Vessel v, VesselData vd, VesselResources resources, double elapsed_s)
        {
            // store list of crew to kill
            List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>();

            // get input resource handler
            ResourceInfo res = input.Length > 0 ? resources.GetResource(v, input) : null;

            // determine message variant
            uint variant = vd.EnvTemperature < Settings.LifeSupportSurvivalTemperature ? 0 : 1u;

            // get product of all environment modifiers
            double k = Modifiers.Evaluate(v, vd, resources, modifiers);

            bool lifetime_enabled = PreferencesRadiation.Instance.lifetime;

            // for each crew
            foreach (ProtoCrewMember c in Lib.CrewList(v))
            {
                // get kerbal data
                KerbalData kd = DB.Kerbal(c.name);

                // skip rescue kerbals
                if (kd.rescue)
                {
                    continue;
                }

                // skip disabled kerbals
                if (kd.disabled)
                {
                    continue;
                }

                // get kerbal property data from db
                RuleData rd = kd.Rule(name);
                rd.lifetime = lifetime_enabled && lifetime;

                // influence consumption by elapsed time
                double step = elapsed_s;

                // if interval-based
                if (interval > 0.0)
                {
                    // accumulate time
                    rd.time_since += elapsed_s;

                    // determine number of intervals that has passed (can be 2 or more if elapsed_s > interval * 2)
                    step = Math.Floor(rd.time_since / interval);

                    // consume time
                    rd.time_since -= step * interval;
                }

                // if there is a resource specified
                if (res != null && rate > double.Epsilon)
                {
                    // get rate including per-kerbal variance
                    double resRate =
                        rate                                                        // consumption rate
                        * Variance(name, c, individuality)                          // kerbal-specific variance
                        * k;                                                        // product of environment modifiers

                    // determine amount of resource to consume

                    double required = resRate * step;                           // seconds elapsed or interval amount

                    // remember if a meal is consumed/produced in this simulation step
                    if (interval > 0.0)
                    {
                        double ratePerStep = resRate / interval;
                        res.UpdateIntervalRule(-required, -ratePerStep, name);
                        if (output.Length > 0)
                        {
                            ResourceCache.GetResource(v, output).UpdateIntervalRule(required * ratio, ratePerStep * ratio, name);
                        }
                    }

                    // if continuous, or if one or more intervals elapsed
                    if (step > 0.0)
                    {
                        // if there is no output
                        if (output.Length == 0)
                        {
                            // simply consume (that is faster)
                            res.Consume(required, name);
                        }
                        // if there is an output
                        else
                        {
                            // transform input into output resource
                            // - rules always dump excess overboard (because it is waste)
                            ResourceRecipe recipe = new ResourceRecipe(name);
                            recipe.AddInput(input, required);
                            recipe.AddOutput(output, required * ratio, true);
                            resources.AddRecipe(recipe);
                        }
                    }
                }

                // if continuous, or if one or more intervals elapsed
                if (step > 0.0)
                {
                    // degenerate:
                    // - if the environment modifier is not telling to reset (by being zero)
                    // - if this rule is resource-less, or if there was not enough resource in the vessel
                    if (k > 0.0 && (input.Length == 0 || res.Amount <= double.Epsilon))
                    {
                        rd.problem += degeneration                                   // degeneration rate per-second or per-interval
                                      * k                                            // product of environment modifiers
                                      * step                                         // seconds elapsed or by number of steps
                                      * Variance(name, c, variance);                 // kerbal-specific variance
                    }
                    // else slowly recover
                    else
                    {
                        rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002);
                    }
                }

                bool do_breakdown = false;

                if (breakdown)
                {
                    // don't do breakdowns and don't show stress message if disabled
                    if (!PreferencesComfort.Instance.stressBreakdowns)
                    {
                        return;
                    }

                    // stress level
                    double breakdown_probability = rd.problem / warning_threshold;
                    breakdown_probability = Lib.Clamp(breakdown_probability, 0.0, 1.0);

                    // use the stupidity of a kerbal.
                    // however, nobody is perfect - not even a kerbal with a stupidity of 0.
                    breakdown_probability *= c.stupidity * 0.6 + 0.4;

                    // apply the weekly error rate
                    breakdown_probability *= PreferencesComfort.Instance.stressBreakdownRate;

                    // now we have the probability for one failure per week, based on the
                    // individual stupidity and stress level of the kerbal.

                    breakdown_probability = (breakdown_probability * elapsed_s) / (Lib.DaysInYear * Lib.HoursInDay * 3600);
                    if (breakdown_probability > Lib.RandomDouble())
                    {
                        do_breakdown = true;

                        // we're stressed out and just made a major mistake, this further increases the stress level...
                        rd.problem += warning_threshold * 0.05;                         // add 5% of the warning treshold to current stress level
                    }
                }

                // kill kerbal if necessary
                if (rd.problem >= fatal_threshold)
                {
#if DEBUG || DEVBUILD
                    Lib.Log("Rule " + name + " kills " + c.name + " at " + rd.problem + " " + degeneration + "/" + k + "/" + step + "/" + Variance(name, c, variance));
#endif
                    if (fatal_message.Length > 0)
                    {
                        Message.Post(breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(fatal_message, v, c, variant));
                    }

                    if (breakdown)
                    {
                        do_breakdown = true;

                        // move back between warning and danger level
                        rd.problem = (warning_threshold + danger_threshold) * 0.5;

                        // make sure next danger message is shown
                        rd.message = 1;
                    }
                    else
                    {
                        deferred_kills.Add(c);
                    }
                }
                // show messages
                else if (rd.problem >= danger_threshold && rd.message < 2)
                {
                    if (danger_message.Length > 0)
                    {
                        Message.Post(Severity.danger, Lib.ExpandMsg(danger_message, v, c, variant));
                    }
                    rd.message = 2;
                }
                else if (rd.problem >= warning_threshold && rd.message < 1)
                {
                    if (warning_message.Length > 0)
                    {
                        Message.Post(Severity.warning, Lib.ExpandMsg(warning_message, v, c, variant));
                    }
                    rd.message = 1;
                }
                else if (rd.problem < warning_threshold && rd.message > 0)
                {
                    if (relax_message.Length > 0)
                    {
                        Message.Post(Severity.relax, Lib.ExpandMsg(relax_message, v, c, variant));
                    }
                    rd.message = 0;
                }

                if (do_breakdown)
                {
                    // trigger breakdown event
                    Misc.Breakdown(v, c);
                }
            }

            // execute the deferred kills
            foreach (ProtoCrewMember c in deferred_kills)
            {
                Misc.Kill(v, c);
            }
        }
Example #27
0
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

            // if enabled
            if (running)
            {
                // if a researcher is not required, or the researcher is present
                if (!researcher_cs || researcher_cs.Check(part.protoModuleCrew))
                {
                    // get next sample to analyze
                    current_sample = NextSample(vessel);

                    double rate = analysis_rate;
                    if (researcher_cs)
                    {
                        int    bonus     = researcher_cs.Bonus(part.protoModuleCrew);
                        double crew_gain = 1 + bonus * Settings.LaboratoryCrewLevelBonus;
                        crew_gain = Lib.Clamp(crew_gain, 1, Settings.MaxLaborartoryBonus);
                        rate     *= crew_gain;
                    }

                    // if there is a sample to analyze
                    if (current_sample != null)
                    {
                        // consume EC
                        ec = ResourceCache.Info(vessel, "ElectricCharge");
                        ec.Consume(ec_rate * Kerbalism.elapsed_s);

                        // if there was ec
                        // - comparing against amount in previous simulation step
                        if (ec.amount > double.Epsilon)
                        {
                            // analyze the sample
                            status  = Analyze(vessel, current_sample, rate * Kerbalism.elapsed_s);
                            running = status == Status.RUNNING;
                        }
                        // if there was no ec
                        else
                        {
                            status = Status.NO_EC;
                        }
                    }
                    // if there is no sample to analyze
                    else
                    {
                        status = Status.NO_SAMPLE;
                    }
                }
                // if a researcher is required, but missing
                else
                {
                    status = Status.NO_RESEARCHER;
                }
            }
            // if disabled
            else
            {
                status = Status.DISABLED;
            }
        }
Example #28
0
 /// <summary>
 /// Set the current sun observation quality (ranges from 0 to 1). this is
 /// the probability that the player will get a warning for an incoming CME
 /// </summary>
 /// <param name="quality">Quality.</param>
 public static void SetStormObservationQuality(float quality)
 {
     Storm.sun_observation_quality = Lib.Clamp(quality, 0.0f, 1.0f);
 }
Example #29
0
		// 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);
		}
Example #30
0
 public override void ctrl(double value)
 {
   Lib.Proto.Set(ring, "speed", (float)Lib.Clamp(value, 0.0, 1.0));
   Lib.Proto.Set(ring, "opened", true);
 }