Exemplo n.º 1
0
  void indicator_supplies(Panel p, Vessel v, vessel_info vi)
  {
    List<string> tooltips = new List<string>();
    uint max_severity = 0;
    if (vi.crew_count > 0)
    {
      var supplies = Profile.supplies.FindAll(k => k.resource != "ElectricCharge");
      foreach(Supply supply in supplies)
      {
        resource_info res = ResourceCache.Info(v, supply.resource);
        if (res.capacity > double.Epsilon)
        {
          double depletion = res.Depletion(vi.crew_count);
          string deplete_str = depletion <= double.Epsilon
            ? ", depleted"
            : double.IsNaN(depletion)
            ? ""
            : Lib.BuildString(", deplete in <b>", Lib.HumanReadableDuration(depletion), "</b>");
          tooltips.Add(Lib.BuildString(supply.resource, ": <b>", Lib.HumanReadablePerc(res.level), "</b>", deplete_str));

          uint severity = res.level <= 0.005 ? 2u : res.level <= supply.low_threshold ? 1u : 0;
          max_severity = Math.Max(max_severity, severity);
        }
      }
    }

    Texture image = Icons.box_white;
    switch(max_severity)
    {
      case 0: image = Icons.box_white; break;
      case 1: image = Icons.box_yellow; break;
      case 2: image = Icons.box_red;  break;
    }
    string tooltip = string.Join("\n", tooltips.ToArray());

    p.icon(image, tooltip);
  }
Exemplo n.º 2
0
        void Indicator_supplies(Panel p, Vessel v, VesselData vd)
        {
            List <string> tooltips     = new List <string>();
            uint          max_severity = 0;

            if (vd.CrewCount > 0)
            {
                foreach (Supply supply in Profile.supplies.FindAll(k => k.resource != "ElectricCharge"))
                {
                    ResourceInfo res       = ResourceCache.GetResource(v, supply.resource);
                    double       depletion = res.DepletionTime();

                    if (res.Capacity > double.Epsilon)
                    {
                        if (tooltips.Count == 0)
                        {
                            tooltips.Add(String.Format("<align=left /><b>{0,-18}\t" + Local.Monitor_level + "\t" + Local.Monitor_duration + "</b>", Local.Monitor_name));                                             //level"duration"name"
                        }
                        tooltips.Add(Lib.Color(
                                         String.Format("{0,-18}\t{1}\t{2}", supply.resource, Lib.HumanReadablePerc(res.Level), depletion <= double.Epsilon ? Local.Monitor_depleted : Lib.HumanReadableDuration(depletion)),               //"depleted"
                                         res.Level <= 0.005 ? Lib.Kolor.Red : res.Level <= supply.low_threshold ? Lib.Kolor.Orange : Lib.Kolor.None
                                         ));

                        uint severity = res.Level <= 0.005 ? 2u : res.Level <= supply.low_threshold ? 1u : 0;
                        max_severity = Math.Max(max_severity, severity);
                    }
                }
            }

            Texture2D image = max_severity == 2
                          ? Textures.box_red
                          : max_severity == 1
                          ? Textures.box_yellow
                          : Textures.box_white;

            p.AddRightIcon(image, string.Join("\n", tooltips.ToArray()));
        }
Exemplo n.º 3
0
  public void FixedUpdate()
  {
    // in any scene: update the RMB ui
    Status = Lib.HumanReadableRadiationRate(Math.Abs(radiation) * intensity);

    // do nothing else in the editor
    if (HighLogic.LoadedSceneIsEditor) return;

    // if there is ec consumption
    if (ec_rate > double.Epsilon)
    {
      // get vessel info from the cache
      vessel_info vi = Cache.VesselInfo(vessel);

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

      // get resource cache
      resource_info ec = ResourceCache.Info(vessel, "ElectricCharge");

      // get elapsed time
      double elapsed_s = Kerbalism.elapsed_s * vi.time_dilation;

      // if there is enough EC
      // note: comparing against amount in previous simulation step
      if (ec.amount > double.Epsilon)
      {
        // consume EC
        ec.Consume(ec_rate * intensity * elapsed_s);
      }
      // else disable it
      else
      {
        intensity = 0.0f;
      }
    }
  }
Exemplo n.º 4
0
        // return total radiation emitted in a vessel
        public static double Total(Vessel v)
        {
            // get resource cache
            ResourceInfo ec = ResourceCache.GetResource(v, "ElectricCharge");

            double total = 0.0;

            if (v.loaded)
            {
                foreach (var shield in Lib.FindModules <PassiveShield>(v))
                {
                    if (ec.Amount > 0 || shield.ec_rate <= 0)
                    {
                        if (shield.deployed)
                        {
                            total += shield.radiation;
                        }
                    }
                }
            }
            else
            {
                foreach (ProtoPartModuleSnapshot m in Lib.FindModules(v.protoVessel, "PassiveShield"))
                {
                    if (ec.Amount > 0 || Lib.Proto.GetDouble(m, "ec_rate") <= 0)
                    {
                        if (Lib.Proto.GetBool(m, "deployed"))
                        {
                            var rad = Lib.Proto.GetDouble(m, "radiation");
                            total += rad;
                        }
                    }
                }
            }
            return(total);
        }
Exemplo n.º 5
0
        // return name of file being transmitted from vessel specified
        public static string Transmitting(Vessel v, bool linked)
        {
            // never transmitting if science system is disabled
            if (!Features.Science)
            {
                return(string.Empty);
            }

            // not transmitting if unlinked
            if (!linked)
            {
                return(string.Empty);
            }

            // not transmitting if there is no ec left
            if (ResourceCache.Info(v, "ElectricCharge").amount <= double.Epsilon)
            {
                return(string.Empty);
            }

            // get first file flagged for transmission, AND has a ts at least 5 seconds old or is > 0.001Mb in size
            foreach (var drive in DB.Vessel(v).drives.Values)
            {
                double now = Planetarium.GetUniversalTime();
                foreach (var p in drive.files)
                {
                    if (p.Value.send && (p.Value.ts + 3 < now || p.Value.size > 0.003))
                    {
                        return(p.Key);
                    }
                }
            }

            // no file flagged for transmission
            return(string.Empty);
        }
Exemplo n.º 6
0
        public void Update()
        {
            // in editor, merely update ui button label
            if (Lib.IsEditor())
            {
                Events["Toggle"].guiName = Lib.StatusToggle(title, running ? "running" : "stopped");
            }

            // if in flight, and the stock planet resource system is online
            if (Lib.IsFlight() && ResourceMap.Instance != null)
            {
                // sample abundance
                double abundance = SampleAbundance();

                // determine if resource can be extracted
                issue = DetectIssue(abundance);

                // TODO: Change Review
                double ecLeft = ResourceCache.Info(part.vessel, "ElectricCharge").amount;

                // update ui
                Events["Toggle"].guiActive    = deployed && ecLeft > double.Epsilon;
                Fields["Abundance"].guiActive = deployed && ecLeft > double.Epsilon;
                if (deployed)
                {
                    string status = !running
            ? "stopped"
            : issue.Length == 0
            ? "running"
            : Lib.BuildString("<color=yellow>", issue, "</color>");

                    Events["Toggle"].guiName = Lib.StatusToggle(title, status);
                    Abundance = abundance > double.Epsilon ? Lib.HumanReadablePerc(abundance, "F2") : "none";
                }
            }
        }
Exemplo n.º 7
0
        public void Execute(Vessel v, ScriptType type)
        {
            // do nothing if there is no EC left on the vessel
            Resource_Info ec = ResourceCache.Info(v, "ElectricCharge");

            if (ec.amount <= double.Epsilon)
            {
                return;
            }

            // get the script
            if (scripts.TryGetValue(type, out Script script))
            {
                // execute the script
                script.Execute(Boot(v));

                // show message to the user
                // - unless the script is empty (can happen when being edited)
                if (script.states.Count > 0 && DB.Vessel(v).cfg_script)
                {
                    Message.Post(Lib.BuildString("Script called on vessel <b>", v.vesselName, "</b>"));
                }
            }
        }
Exemplo n.º 8
0
		void Indicator_supplies(Panel p, Vessel v, Vessel_info vi)
		{
			List<string> tooltips = new List<string>();
			uint max_severity = 0;
			if (vi.crew_count > 0)
			{
				foreach (Supply supply in Profile.supplies.FindAll(k => k.resource != "ElectricCharge"))
				{
					Resource_info res = ResourceCache.Info(v, supply.resource);
					double depletion = res.Depletion(vi.crew_count);

					if (res.capacity > double.Epsilon)
					{
						if (tooltips.Count == 0) tooltips.Add(String.Format("<align=left /><b>{0,-18}\tlevel\tduration</b>", "name"));
						tooltips.Add(Lib.BuildString
						(
						  res.level <= 0.005 ? "<color=#ff0000>" : res.level <= supply.low_threshold ? "<color=#ffff00>" : "<color=#cccccc>",
						  String.Format("{0,-18}\t{1}\t{2}", supply.resource, Lib.HumanReadablePerc(res.level),
						  depletion <= double.Epsilon ? "depleted" : Lib.HumanReadableDuration(depletion)),
						  "</color>"
						));

						uint severity = res.level <= 0.005 ? 2u : res.level <= supply.low_threshold ? 1u : 0;
						max_severity = Math.Max(max_severity, severity);
					}
				}
			}

			Texture2D image = max_severity == 2
			  ? Icons.box_red
			  : max_severity == 1
			  ? Icons.box_yellow
			  : Icons.box_white;

			p.AddIcon(image, string.Join("\n", tooltips.ToArray()));
		}
Exemplo n.º 9
0
        void VesselDestroyed(Vessel v)
        {
            DB.vessels.Remove(Lib.VesselID(v));

            // rescan the damn kerbals
            // - vessel crew is empty at destruction time
            // - we can't even use the flightglobal roster, because sometimes it isn't updated yet at this point
            HashSet <string> kerbals_alive = new HashSet <string>();
            HashSet <string> kerbals_dead  = new HashSet <string>();

            foreach (Vessel ov in FlightGlobals.Vessels)
            {
                foreach (ProtoCrewMember c in Lib.CrewList(ov))
                {
                    kerbals_alive.Add(c.name);
                }
            }
            foreach (KeyValuePair <string, KerbalData> p in DB.Kerbals())
            {
                if (!kerbals_alive.Contains(p.Key))
                {
                    kerbals_dead.Add(p.Key);
                }
            }
            foreach (string n in kerbals_dead)
            {
                // we don't know if the kerbal really is dead, or if it is just not currently assigned to a mission
                DB.KillKerbal(n, false);
            }


            // purge the caches
            ResourceCache.Purge(v);
            Drive.Purge(v);
            Cache.PurgeObjects(v);
        }
Exemplo n.º 10
0
        public void FixedUpdate()
        {
            // in flight
            if (Lib.IsFlight())
            {
                // if we are transmitting using the stock system
                if (stream.transmitting())
                {
                    // get ec resource handler
                    resource_info ec = ResourceCache.Info(vessel, "ElectricCharge");

                    // if we are still linked, and there is ec left
                    if (CanTransmit() && ec.amount > double.Epsilon)
                    {
                        // compression factor
                        // - used to avoid making the user wait too much for transmissions that
                        //   don't happen in background, while keeping transmission rates realistic
                        const double compression = 16.0;

                        // transmit using the data stream
                        stream.update(DataRate * Kerbalism.elapsed_s * compression, vessel);

                        // consume ec
                        ec.Consume(DataResourceCost * Kerbalism.elapsed_s);
                    }
                    else
                    {
                        // abort transmission, return data to the vessel
                        stream.abort(vessel);

                        // inform the user
                        ScreenMessages.PostScreenMessage("Transmission aborted", 5.0f, ScreenMessageStyle.UPPER_LEFT);
                    }
                }
            }
        }
Exemplo n.º 11
0
  public static void telemetry(this Panel p, Vessel v)
  {
    // if vessel doesn't exist anymore
    if (FlightGlobals.FindVessel(v.id) == null) return;

    // get info from the cache
    vessel_info vi = Cache.VesselInfo(v);

    // if not a valid vessel
    if (!vi.is_valid) return;

    // set metadata
    p.title(Lib.BuildString(Lib.Ellipsis(v.vesselName, 20), " <color=#cccccc>TELEMETRY</color>"));

    // time-out simulation
    if (p.timeout(vi)) return;

    // get vessel data
    VesselData vd = DB.Vessel(v);

    // get resources
    vessel_resources resources = ResourceCache.Get(v);

    // get crew
    var crew = Lib.CrewList(v);

    // draw the content
    render_crew(p, crew);
    render_greenhouse(p, vi);
    render_supplies(p, v, vi, resources);
    render_habitat(p, v, vi);
    render_environment(p, v, vi);

    // collapse eva kerbal sections into one
    if (v.isEVA) p.collapse("EVA SUIT");
  }
Exemplo n.º 12
0
        void manageResqueMission(Vessel v)
        {
            // true if we detected this was a resque mission vessel
            bool detected = false;

            // deal with resque missions
            foreach (ProtoCrewMember c in v.GetVesselCrew())
            {
                // get kerbal data
                kerbal_data kd = DB.KerbalData(c.name);

                // flag the kerbal as not resque at prelaunch
                if (v.situation == Vessel.Situations.PRELAUNCH)
                {
                    kd.resque = 0;
                }

                // if the kerbal belong to a resque mission
                if (kd.resque == 1)
                {
                    // remember it
                    detected = true;

                    // flag the kerbal as non-resque
                    // note: enable life support mechanics for the kerbal
                    kd.resque = 0;

                    // show a message
                    Message.Post(Lib.BuildString("We found <b>", c.name, "</b>"), Lib.BuildString((c.gender == ProtoCrewMember.Gender.Male ? "He" : "She"), "'s still alive!"));
                }
            }

            // gift resources
            if (detected)
            {
                var reslib = PartResourceLibrary.Instance.resourceDefinitions;
                var parts  = Lib.GetPartsRecursively(v.rootPart);

                // give the vessel some monoprop
                string monoprop_name = v.isEVA ? "EVA Propellant" : detected_mods.RealFuels ? "Hydrazine" : "MonoPropellant";
                foreach (var part in parts)
                {
                    if (part.CrewCapacity > 0 || part.FindModuleImplementing <KerbalEVA>() != null)
                    {
                        if (part.Resources.list.Find(k => k.resourceName == monoprop_name) == null)
                        {
                            Lib.SetupResource(part, monoprop_name, 0.0, Settings.MonoPropellantOnResque);
                        }
                        break;
                    }
                }
                ResourceCache.Produce(v, monoprop_name, Settings.MonoPropellantOnResque);

                // give the vessel some supplies
                foreach (Rule r in rules)
                {
                    if (r.resource_name.Length == 0 || r.on_resque <= double.Epsilon || !reslib.Contains(r.resource_name))
                    {
                        continue;
                    }
                    foreach (var part in parts)
                    {
                        if (part.CrewCapacity > 0 || part.FindModuleImplementing <KerbalEVA>() != null)
                        {
                            if (part.Resources.list.Find(k => k.resourceName == r.resource_name) == null)
                            {
                                Lib.SetupResource(part, r.resource_name, 0.0, r.on_resque);
                            }
                            break;
                        }
                    }
                    ResourceCache.Produce(v, r.resource_name, r.on_resque);
                }
            }
        }
Exemplo n.º 13
0
        void toEVA(GameEvents.FromToAction <Part, Part> data)
        {
            // use Hydrazine instead of MonoPropellant if RealFuel is installed
            string monoprop_name = detected_mods.RealFuels ? "Hydrazine" : "MonoPropellant";

            // determine if inside breathable atmosphere
            // note: the user can force the helmet + oxygen by pressing shift when going on eva
            bool breathable = Sim.Breathable(data.from.vessel) && !(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift));

            // get total crew in the origin vessel
            double tot_crew = (double)data.from.vessel.GetVesselCrew().Count + 1.0;

            // EVA vessels start with 5 units of eva fuel, remove them
            data.to.RequestResource("EVA Propellant", 5.0);

            // determine how much MonoPropellant to get
            // note: never more that the 'share' of this kerbal
            double monoprop = Math.Min(ResourceCache.Info(data.from.vessel, monoprop_name).amount / tot_crew, Settings.MonoPropellantOnEVA);

            // get monoprop from the vessel
            monoprop = data.from.RequestResource(monoprop_name, monoprop);

            // transfer monoprop to the EVA kerbal
            data.to.RequestResource("EVA Propellant", -monoprop);

            // show warning if there isn't monoprop in the eva suit
            if (monoprop <= double.Epsilon && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", monoprop_name, "</b> in the EVA suit", "Don't let the ladder go!"));
            }

            // manage resources from rules
            foreach (Rule r in rules)
            {
                if (r.resource_name.Length == 0 || r.on_eva <= double.Epsilon)
                {
                    continue;
                }

                // determine amount to take, never more that his own share
                double amount = Math.Min(ResourceCache.Info(data.from.vessel, r.resource_name).amount / tot_crew, r.on_eva);

                // deal with breathable modifier
                if (breathable && r.modifier.Contains("breathable"))
                {
                    continue;
                }

                // remove resource from the vessel
                amount = data.from.RequestResource(r.resource_name, amount);

                // create new resource in the eva kerbal
                Lib.SetupResource(data.to, r.resource_name, amount, r.on_eva);
            }

            // get KerbalEVA
            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();

            // turn off headlamp light, to avoid stock bug that show the light for a split second when going on eva
            EVA.SetHeadlamp(kerbal, false);
            EVA.SetFlares(kerbal, false);

            // remove the helmet if inside breathable atmosphere
            // note: done in EVA::FixedUpdate(), but also done here avoid 'popping' of the helmet when going on eva
            EVA.SetHelmet(kerbal, !breathable);

            // remember if the kerbal has an helmet
            EVA.KerbalData(data.to.vessel).has_helmet = !breathable;

            // execute script on vessel computer
            if (DB.Ready())
            {
                DB.VesselData(data.from.vessel.id).computer.execute("run", "auto/eva_out", string.Empty, data.from.vessel);
            }

            // mute messages for a couple seconds to avoid warning messages from the vessel resource amounts
            Message.MuteInternal();
            base.StartCoroutine(CallbackUtil.DelayedCallback(2.0f, Message.UnmuteInternal));

            // if vessel info is open, switch to the eva kerbal
            // note: for a single tick, the EVA vessel is not valid (sun_dist is zero)
            // this make IsVessel() return false, that in turn close the vessel info instantly
            // for this reason, we wait a small amount of time before switching the info window
            if (Info.IsOpen())
            {
                Info.Open(data.to.vessel);
            }
        }
Exemplo n.º 14
0
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

            // if enabled and not ready for harvest
            if (active && growth < 0.99)
            {
                // get vessel info from the cache
                // - if the vessel is not valid (eg: flagged as debris) then solar flux will be 0 and landed false (but that's okay)
                Vessel_info vi = Cache.VesselInfo(vessel);

                // get resource cache
                Vessel_resources resources = ResourceCache.Get(vessel);
                Resource_info    ec        = resources.Info(vessel, "ElectricCharge");

                // deal with corner cases when greenhouse is assembled using KIS
                if (double.IsNaN(growth) || double.IsInfinity(growth))
                {
                    growth = 0.0;
                }

                // calculate natural and artificial lighting
                natural    = vi.solar_flux;
                artificial = Math.Max(light_tolerance - natural, 0.0);

                // consume EC for the lamps, scaled by artificial light intensity
                if (artificial > double.Epsilon)
                {
                    ec.Consume(ec_rate * (artificial / light_tolerance) * Kerbalism.elapsed_s, "greenhouse");
                }

                // reset artificial lighting if there is no ec left
                // - comparing against amount in previous simulation step
                if (ec.amount <= double.Epsilon)
                {
                    artificial = 0.0;
                }

                // execute recipe
                Resource_recipe recipe = new Resource_recipe(part, "greenhouse");
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    // WasteAtmosphere is primary combined input
                    if (WACO2 && input.name == "WasteAtmosphere")
                    {
                        recipe.Input(input.name, vi.breathable ? 0.0 : input.rate * Kerbalism.elapsed_s, "CarbonDioxide");
                    }
                    // CarbonDioxide is secondary combined input
                    else if (WACO2 && input.name == "CarbonDioxide")
                    {
                        recipe.Input(input.name, vi.breathable ? 0.0 : input.rate * Kerbalism.elapsed_s, "");
                    }
                    // if atmosphere is breathable disable WasteAtmosphere / CO2
                    else if (!WACO2 && (input.name == "CarbonDioxide" || input.name == "WasteAtmosphere"))
                    {
                        recipe.Input(input.name, vi.breathable ? 0.0 : input.rate, "");
                    }
                    else
                    {
                        recipe.Input(input.name, input.rate * Kerbalism.elapsed_s);
                    }
                }
                foreach (ModuleResource output in resHandler.outputResources)
                {
                    // if atmosphere is breathable disable Oxygen
                    if (output.name == "Oxygen")
                    {
                        recipe.Output(output.name, vi.breathable ? 0.0 : output.rate * Kerbalism.elapsed_s, true);
                    }
                    else
                    {
                        recipe.Output(output.name, output.rate * Kerbalism.elapsed_s, true);
                    }
                }
                resources.Transform(recipe);

                // determine environment conditions
                bool lighting  = natural + artificial >= light_tolerance;
                bool pressure  = pressure_tolerance <= double.Epsilon || vi.pressure >= pressure_tolerance;
                bool radiation = radiation_tolerance <= double.Epsilon || vi.radiation * (1.0 - vi.shielding) < radiation_tolerance;

                // determine input resources conditions
                // - comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                bool   dis_WACO2   = false;
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    // combine WasteAtmosphere and CO2 if both exist
                    if (input.name == "WasteAtmosphere" || input.name == "CarbonDioxide")
                    {
                        if (dis_WACO2 || Cache.VesselInfo(vessel).breathable)
                        {
                            continue;                                                                              // skip if already checked or atmosphere is breathable
                        }
                        if (WACO2)
                        {
                            if (resources.Info(vessel, "WasteAtmosphere").amount <= double.Epsilon && resources.Info(vessel, "CarbonDioxide").amount <= double.Epsilon)
                            {
                                inputs      = false;
                                missing_res = "CarbonDioxide";
                                break;
                            }
                            dis_WACO2 = true;
                            continue;
                        }
                    }
                    if (resources.Info(vessel, input.name).amount <= double.Epsilon)
                    {
                        inputs      = false;
                        missing_res = input.name;
                        break;
                    }
                }

                // if growing
                if (lighting && pressure && radiation && inputs)
                {
                    // increase growth
                    growth += crop_rate * Kerbalism.elapsed_s;
                    growth  = Math.Min(growth, 1.0);

                    // notify the user when crop can be harvested
                    if (growth >= 0.99)
                    {
                        Message.Post(Lib.BuildString("On <b>", vessel.vesselName, "</b> the crop is ready to be harvested"));
                        growth = 1.0;
                    }
                }

                // update time-to-harvest
                tta = (1.0 - growth) / crop_rate;

                // update issues
                issue =
                    !inputs?Lib.BuildString("missing ", missing_res)
                        : !lighting ? "insufficient lighting"
                                : !pressure ? "insufficient pressure"
                                : !radiation ? "excessive radiation"
                                : string.Empty;
            }
        }
Exemplo n.º 15
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
                    string sample_filename = next_sample(vessel);

                    // if there is a sample to analyze
                    if (sample_filename.Length > 0)
                    {
                        // consume EC
                        resource_info 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
                            analyze(vessel, sample_filename, analysis_rate * Kerbalism.elapsed_s);

                            // update status
                            status = Science.experiment(sample_filename).name;
                        }
                        // if there was no ec
                        else
                        {
                            // update status
                            status = "<color=yellow>no electric charge</color>";
                        }
                    }
                    // if there is no sample to analyze
                    else
                    {
                        // update status
                        status = "no samples to analyze";
                    }
                }
                // if a researcher is required, but missing
                else
                {
                    // update status
                    status = Lib.BuildString("<color=yellow>", researcher_cs.warning(), "</color>");
                }
            }
            // if disabled
            else
            {
                // update status
                status = "disabled";
            }
        }
Exemplo n.º 16
0
        public void FixedUpdate()
        {
            // basic sanity checks
            if (Lib.IsEditor())
            {
                return;
            }
            if (!Cache.VesselInfo(vessel).is_valid)
            {
                return;
            }
            if (next_check > Planetarium.GetUniversalTime())
            {
                return;
            }

            // get ec handler
            Resource_info ec = ResourceCache.Info(vessel, "ElectricCharge");

            shrouded = part.ShieldedFromAirstream;
            issue    = TestForIssues(vessel, ec, this, privateHdId, broken,
                                     remainingSampleMass, didPrepare, shrouded, last_subject_id);

            if (string.IsNullOrEmpty(issue))
            {
                issue = TestForResources(vessel, resourceDefs, Kerbalism.elapsed_s, ResourceCache.Get(vessel));
            }

            scienceValue = Science.Value(last_subject_id, 0);
            state        = GetState(scienceValue, issue, recording, forcedRun);

            if (!string.IsNullOrEmpty(issue))
            {
                next_check = Planetarium.GetUniversalTime() + Math.Max(3, Kerbalism.elapsed_s * 3);
                return;
            }

            var subject_id = Science.Generate_subject_id(experiment_id, vessel);

            if (last_subject_id != subject_id)
            {
                dataSampled = 0;
                forcedRun   = false;
            }
            last_subject_id = subject_id;

            if (state != State.RUNNING)
            {
                return;
            }

            var exp = Science.Experiment(experiment_id);

            if (dataSampled >= exp.max_amount)
            {
                return;
            }

            // if experiment is active and there are no issues
            DoRecord(ec, subject_id);
        }
Exemplo n.º 17
0
        void FixedUpdate()
        {
            // remove control locks in any case
            Misc.clearLocks();

            // do nothing if paused
            if (Lib.IsPaused())
            {
                return;
            }

            // maintain elapsed_s, converting to double only once
            // and detect warp blending
            double fixedDeltaTime = TimeWarp.fixedDeltaTime;

            if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon)
            {
                warp_blending = 0;
            }
            else
            {
                ++warp_blending;
            }
            elapsed_s = fixedDeltaTime;

            // evict oldest entry from vessel cache
            Cache.update();

            // store info for oldest unloaded vessel
            double           last_time      = 0.0;
            Vessel           last_v         = null;
            vessel_info      last_vi        = null;
            VesselData       last_vd        = null;
            vessel_resources last_resources = null;

            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // get vessel info from the cache
                vessel_info vi = Cache.VesselInfo(v);

                // set locks for active vessel
                if (v.isActiveVessel)
                {
                    Misc.setLocks(v, vi);
                }

                // maintain eva dead animation and helmet state
                if (v.loaded && v.isEVA)
                {
                    EVA.update(v);
                }

                // keep track of rescue mission kerbals, and gift resources to their vessels on discovery
                if (v.loaded && vi.is_vessel)
                {
                    // manage rescue mission mechanics
                    Misc.manageRescueMission(v);
                }

                // do nothing else for invalid vessels
                if (!vi.is_valid)
                {
                    continue;
                }

                // get vessel data from db
                VesselData vd = DB.Vessel(v);

                // get resource cache
                vessel_resources resources = ResourceCache.Get(v);

                // if loaded
                if (v.loaded)
                {
                    // show belt warnings
                    Radiation.beltWarnings(v, vi, vd);

                    // update storm data
                    Storm.update(v, vi, vd, elapsed_s);

                    // show signal warnings
                    Signal.update(v, vi, vd, elapsed_s);

                    // consume ec for transmission, and transmit science data
                    Science.update(v, vi, vd, resources, elapsed_s);

                    // apply rules
                    Profile.Execute(v, vi, vd, resources, elapsed_s);

                    // apply deferred requests
                    resources.Sync(v, elapsed_s);

                    // call automation scripts
                    vd.computer.automate(v, vi, resources);

                    // remove from unloaded data container
                    unloaded.Remove(vi.id);
                }
                // if unloaded
                else
                {
                    // get unloaded data, or create an empty one
                    unloaded_data ud;
                    if (!unloaded.TryGetValue(vi.id, out ud))
                    {
                        ud = new unloaded_data();
                        unloaded.Add(vi.id, ud);
                    }

                    // accumulate time
                    ud.time += elapsed_s;

                    // maintain oldest entry
                    if (ud.time > last_time)
                    {
                        last_time      = ud.time;
                        last_v         = v;
                        last_vi        = vi;
                        last_vd        = vd;
                        last_resources = resources;
                    }
                }
            }


            // if the oldest unloaded vessel was selected
            if (last_v != null)
            {
                // show belt warnings
                Radiation.beltWarnings(last_v, last_vi, last_vd);

                // update storm data
                Storm.update(last_v, last_vi, last_vd, last_time);

                // show signal warnings
                Signal.update(last_v, last_vi, last_vd, last_time);

                // consume ec for transmission, and transmit science
                Science.update(last_v, last_vi, last_vd, last_resources, last_time);

                // apply rules
                Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time);

                // simulate modules in background
                Background.update(last_v, last_vi, last_vd, last_resources, last_time);

                // apply deferred requests
                last_resources.Sync(last_v, last_time);

                // call automation scripts
                last_vd.computer.automate(last_v, last_vi, last_resources);

                // remove from unloaded data container
                unloaded.Remove(last_vi.id);
            }

            // update storm data for one body per-step
            if (storm_bodies.Count > 0)
            {
                storm_bodies.ForEach(k => k.time += elapsed_s);
                storm_data sd = storm_bodies[storm_index];
                Storm.update(sd.body, sd.time);
                sd.time     = 0.0;
                storm_index = (storm_index + 1) % storm_bodies.Count;
            }
        }
Exemplo n.º 18
0
		// return habitat surface in a vessel in m^2
		public static double tot_surface(Vessel v)
		{
			// we use capacity: this mean that partially pressurized parts will still count,
			return ResourceCache.Info(v, "Shielding").capacity;
		}
Exemplo n.º 19
0
		// return waste level in a vessel atmosphere
		public static double poisoning(Vessel v)
		{
			// the proportion of co2 in the atmosphere is simply the level of WasteAtmo
			return ResourceCache.Info(v, "WasteAtmosphere").level;
		}
Exemplo n.º 20
0
 // return shielding factor in a vessel
 public static double Shielding(Vessel v)
 {
     // the shielding factor is simply the level of shielding, scaled by the 'shielding efficiency' setting
     return(ResourceCache.Info(v, "Shielding").level *PreferencesStorm.Instance.shieldingEfficiency);
 }
Exemplo n.º 21
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);
        }
Exemplo n.º 22
0
        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);
        }
Exemplo n.º 23
0
        // 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);
            }
        }
Exemplo n.º 24
0
        void ToEVA(GameEvents.FromToAction <Part, Part> data)
        {
            OnVesselModified(data.from.vessel);
            OnVesselModified(data.to.vessel);

            // get total crew in the origin vessel
            double tot_crew = Lib.CrewCount(data.from.vessel) + 1.0;

            // get vessel resources handler
            VesselResources resources = ResourceCache.Get(data.from.vessel);

            // setup supply resources capacity in the eva kerbal
            Profile.SetupEva(data.to);

            String prop_name = Lib.EvaPropellantName();

            // for each resource in the kerbal
            for (int i = 0; i < data.to.Resources.Count; ++i)
            {
                // get the resource
                PartResource res = data.to.Resources[i];

                // eva prop is handled differently
                if (res.resourceName == prop_name)
                {
                    continue;
                }

                double quantity = Math.Min(resources.GetResource(data.from.vessel, res.resourceName).Amount / tot_crew, res.maxAmount);
                // remove resource from vessel
                quantity = data.from.RequestResource(res.resourceName, quantity);

                // add resource to eva kerbal
                data.to.RequestResource(res.resourceName, -quantity);
            }

            // take as much of the propellant as possible. just imagine: there are 1.3 units left, and 12 occupants
            // in the ship. you want to send out an engineer to fix the chemical plant that produces monoprop,
            // and have to get from one end of the station to the other with just 0.1 units in the tank...
            // nope.
            double evaPropQuantity = data.from.RequestResource(prop_name, Lib.EvaPropellantCapacity());

            // We can't just add the monoprop here, because that doesn't always work. It might be related
            // to the fact that stock KSP wants to add 5 units of monoprop to new EVAs. Instead of fighting KSP here,
            // we just let it do it's thing and set our amount later in EVA.cs - which seems to work just fine.
            // don't put that into Cache.VesselInfo because that can be deleted before we get there
            Cache.SetVesselObjectsCache(data.to.vessel, "eva_prop", evaPropQuantity);

            // Airlock loss
            resources.Consume(data.from.vessel, "Nitrogen", Settings.LifeSupportAtmoLoss, ResourceBroker.Generic);

            // show warning if there is little or no EVA propellant in the suit
            if (evaPropQuantity <= 0.05 && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger,
                             Local.CallBackMsg_EvaNoMP.Format("<b>" + prop_name + "</b>"), Local.CallBackMsg_EvaNoMP2);       //Lib.BuildString("There isn't any <<1>> in the EVA suit")"Don't let the ladder go!"
            }

            // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva
            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();

            EVA.HeadLamps(kerbal, false);

            // execute script
            data.from.vessel.KerbalismData().computer.Execute(data.from.vessel, ScriptType.eva_out);
        }
Exemplo n.º 25
0
		// return habitat volume in a vessel in m^3
		public static double tot_volume(Vessel v)
		{
			// we use capacity: this mean that partially pressurized parts will still count,
			return ResourceCache.Info(v, "Atmosphere").capacity;
		}
Exemplo n.º 26
0
		public void Update()
		{
			// update RMB ui
			Events["Toggle"].guiName = deployed ? Localizer.Format("#KERBALISM_Generic_RETRACT") : Localizer.Format("#KERBALISM_Generic_DEPLOY");
			Events["Toggle"].active = (deploy.Length > 0) && (part.FindModuleImplementing<Habitat>() == null) && !deploy_anim.Playing() && !waitRotation && ResourceCache.Info(vessel, "ElectricCharge").amount > ec_rate;

			// in flight
			if (Lib.IsFlight())
			{
				// if deployed
				if (deployed)
				{
					// if there is no ec
					if (ResourceCache.Info(vessel, "ElectricCharge").amount < 0.01)
					{
						// pause rotate animation
						// - safe to pause multiple times
						Set_rotation(false);
					}
					// if there is enough ec instead and is not deploying
					else if (Should_start_rotation())
					{
						// resume rotate animation
						// - safe to resume multiple times
						Set_rotation(true);
					}
				}
				// stop loop animation if exist and we are retracting
				else
				{
					// Call transform.stop() if it is rotating and the Stop method wasn't called.
					Set_rotation(false);
				}

				// When is not rotating
				if (waitRotation)
				{
					if (rotateIsTransform && !rotate_transf.IsRotating())
					{
						// start retract animation in the correct direction, when is not rotating
						if (animBackwards) deploy_anim.Play(deployed, false);
						else deploy_anim.Play(!deployed, false);
						waitRotation = false;
					}
					else if (!rotateIsTransform && !rotate_anim.Playing())
					{
						if (animBackwards) deploy_anim.Play(deployed, false);
						else deploy_anim.Play(!deployed, false);
						waitRotation = false;
					}
				}

				if (rotateIsTransform && rotate_transf != null) rotate_transf.DoSpin();
			}
		}
Exemplo n.º 27
0
		// return normalized pressure in a vessel
		public static double pressure(Vessel v)
		{
			// the pressure is simply the atmosphere level
			return ResourceCache.Info(v, "Atmosphere").level;
		}
Exemplo n.º 28
0
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

            // do nothing if vessel is invalid
            if (!Cache.VesselInfo(vessel).is_valid)
            {
                return;
            }

            // get ec handler
            resource_info ec = ResourceCache.Info(vessel, "ElectricCharge");

            // if experiment is active
            if (recording)
            {
                // detect conditions
                // - comparing against amount in previous step
                bool   has_ec       = ec.amount > double.Epsilon;
                bool   has_operator = operator_cs.check(vessel);
                string sit          = Science.situation(vessel, situations);

                // deduce issues
                issue = string.Empty;
                if (sit.Length == 0)
                {
                    issue = "invalid situation";
                }
                else if (!has_operator)
                {
                    issue = "no operator";
                }
                else if (!has_ec)
                {
                    issue = "missing <b>EC</b>";
                }

                // if there are no issues
                if (issue.Length == 0)
                {
                    // generate subject id
                    string subject_id = Science.generate_subject(experiment, vessel.mainBody, sit, Science.biome(vessel, sit), Science.multiplier(vessel, sit));

                    // record in drive
                    if (transmissible)
                    {
                        DB.Vessel(vessel).drive.record_file(subject_id, data_rate * Kerbalism.elapsed_s);
                    }
                    else
                    {
                        DB.Vessel(vessel).drive.record_sample(subject_id, data_rate * Kerbalism.elapsed_s);
                    }

                    // consume ec
                    ec.Consume(ec_rate * Kerbalism.elapsed_s);
                }
            }
        }
Exemplo n.º 29
0
		// return shielding factor in a vessel
		public static double shielding(Vessel v)
		{
			// the shielding factor is simply the level of shielding, scaled by the 'shielding efficiency' setting
			return ResourceCache.Info(v, "Shielding").level * Settings.ShieldingEfficiency;
		}
Exemplo n.º 30
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 < Settings.SurvivalTemperature ? 0 : 1u;

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

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

                // 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
                    res.meal_happened |= step > 0.99;
                    if (output.Length > 0)
                    {
                        ResourceCache.Info(v, output).meal_happened |= step > 0.99;
                    }
                }

                // if continuous, or if one or more intervals elapsed
                if (step > double.Epsilon)
                {
                    // if there is a resource specified
                    if (res != null && rate > double.Epsilon)
                    {
                        // determine amount of resource to consume
                        double required = rate                                    // 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 output_only is false
                        else if (!output_only)
                        {
                            // transform input into output resource
                            // - rules always dump excess overboard (because it is waste)
                            resource_recipe recipe = new resource_recipe();
                            recipe.Input(input, required);
                            recipe.Output(output, required * ratio, true);
                            resources.Transform(recipe);
                        }
                        // if output_only then do not consume input resource
                        else
                        {
                            // simply produce (that is faster)
                            resources.Produce(v, output, required);
                        }
                    }

                    // 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 >= 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(c, variance);                       // kerbal-specific variance
                    }
                    // else slowly recover
                    else
                    {
                        rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002);
                        rd.problem  = Math.Max(rd.problem, 0.0);
                    }
                }

                // 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)
                    {
                        // trigger breakdown event
                        Misc.Breakdown(v, c);

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

                        // make sure next danger messagen 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;
                }
            }

            // execute the deferred kills
            foreach (ProtoCrewMember c in deferred_kills)
            {
                Misc.Kill(v, c);
            }
        }