Beispiel #1
0
        // return true if the vessel is a resque mission
        public static bool IsResqueMission(Vessel v)
        {
            // if db isn't ready, assume a resque mission
            if (!DB.Ready())
            {
                return(true);
            }

            // avoid re-creating dead eva kerbals in the db
            // note: no extra cost if vessel is not eva
            if (EVA.IsDead(v))
            {
                return(true);
            }

            // if at least one of the crew is flagged as resque, consider it a resque mission
            var crew_members = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

            foreach (var c in crew_members)
            {
                kerbal_data kd = DB.KerbalData(c.name);
                if (kd.resque == 1)
                {
                    return(true);
                }
            }

            // not a resque mission
            return(false);
        }
Beispiel #2
0
  void problem_kerbals(List<ProtoCrewMember> crew, ref List<Texture> icons, ref List<string> tooltips)
  {
    UInt32 health_severity = 0;
    UInt32 stress_severity = 0;
    foreach(ProtoCrewMember c in crew)
    {
      // get kerbal data
      kerbal_data kd = DB.KerbalData(c.name);

      // skip disabled kerbals
      if (kd.disabled == 1) continue;

      // health
      if (kd.starved > Settings.StarvedDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is starving"); }
      else if (kd.starved > Settings.StarvedWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " is hungry"); }
      if (kd.deprived > Settings.DeprivedDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is suffocating"); }
      else if (kd.deprived > Settings.DeprivedWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " is gasping"); }
      if (kd.temperature < -Settings.TemperatureDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is freezing"); }
      else if (kd.temperature < -Settings.TemperatureWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " feels cold"); }
      else if (kd.temperature > Settings.TemperatureDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " is burning"); }
      else if (kd.temperature > Settings.TemperatureWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " feels hot"); }

      // radiation
      if (kd.radiation > Settings.RadiationDangerThreshold) { health_severity = Math.Max(health_severity, 2); tooltips.Add(c.name + " exposed to extreme radiation"); }
      else if (kd.radiation > Settings.RadiationWarningThreshold) { health_severity = Math.Max(health_severity, 1); tooltips.Add(c.name + " exposed to intense radiation"); }

      // stress
      if (kd.stressed > Settings.StressedDangerThreshold) { stress_severity = Math.Max(stress_severity, 2); tooltips.Add(c.name + " mind is breaking"); }
      else if (kd.stressed > Settings.StressedWarningThreshold) { stress_severity = Math.Max(stress_severity, 1); tooltips.Add(c.name + " is stressed"); }
    }
    if (health_severity == 1) icons.Add(icon_health_warning);
    else if (health_severity == 2) icons.Add(icon_health_danger);
    if (stress_severity == 1) icons.Add(icon_stress_warning);
    else if (stress_severity == 2) icons.Add(icon_stress_danger);
  }
Beispiel #3
0
  void problem_kerbals(List<ProtoCrewMember> crew, ref List<Texture> icons, ref List<string> tooltips)
  {
    UInt32 health_severity = 0;
    UInt32 stress_severity = 0;
    foreach(ProtoCrewMember c in crew)
    {
      // get kerbal data
      kerbal_data kd = DB.KerbalData(c.name);

      // skip disabled kerbals
      if (kd.disabled == 1) continue;

      foreach(Rule r in Kerbalism.rules)
      {
        kmon_data kmon = DB.KmonData(c.name, r.name);
        if (kmon.problem > r.danger_threshold)
        {
          if (!r.breakdown) health_severity = Math.Max(health_severity, 2);
          else stress_severity = Math.Max(stress_severity, 2);
          tooltips.Add(Lib.BuildString(c.name, " (", r.name, ")"));
        }
        else if (kmon.problem > r.warning_threshold)
        {
          if (!r.breakdown) health_severity = Math.Max(health_severity, 1);
          else stress_severity = Math.Max(stress_severity, 1);
          tooltips.Add(Lib.BuildString(c.name, " (", r.name, ")"));
        }
      }

    }
    if (health_severity == 1) icons.Add(icon_health_warning);
    else if (health_severity == 2) icons.Add(icon_health_danger);
    if (stress_severity == 1) icons.Add(icon_stress_warning);
    else if (stress_severity == 2) icons.Add(icon_stress_danger);
  }
Beispiel #4
0
        void vesselRecovered(ProtoVessel vessel, bool b)
        {
            // note: this is called multiple times when a vessel is recovered

            // for each crew member
            foreach (ProtoCrewMember c in vessel.GetVesselCrew())
            {
                // avoid creating kerbal data in db again,
                // as this function may be called multiple times
                if (!DB.Kerbals().ContainsKey(c.name))
                {
                    continue;
                }

                // set roster status of eva dead kerbals
                if (DB.KerbalData(c.name).eva_dead)
                {
                    c.rosterStatus = ProtoCrewMember.RosterStatus.Dead;
                }

                // forget kerbal data of recovered kerbals
                DB.ForgetKerbal(c.name);
            }

            // TODO: [SCIENCE] add science data from recovered vessel
            // beware of double calls to this function

            // forget vessel data
            DB.ForgetVessel(vessel.vesselID);

            // purge vessel from resource cache
            ResourceCache.Purge(vessel.vesselID);
        }
Beispiel #5
0
  void render_crew(Vessel v, vessel_info vi, List<ProtoCrewMember> crew)
  {
    // get degenerative rules
    List<Rule> degen_rules = Kerbalism.rules.FindAll(k => k.degeneration > 0.0);

    // do nothing if there are no degenerative rules
    if (degen_rules.Count == 0) return;

    // do nothing if there isn't a crew
    if (crew.Count == 0) return;

    // select a kerbal
    ProtoCrewMember kerbal = crew[crew_index % crew.Count];

    // render it
    kerbal_data kd = DB.KerbalData(kerbal.name);
    render_title(Lib.Epsilon(kerbal.name.ToUpper(), 20), ref crew_index, crew.Count);
    foreach(Rule r in degen_rules)
    {
      var kmon = DB.KmonData(kerbal.name, r.name);
      var bar = Lib.ProgressBar(20, kmon.problem, r.warning_threshold, r.danger_threshold, r.fatal_threshold, kd.disabled > 0 ? "cyan" : "");
      render_content(r.name.AddSpacesOnCaps().ToLower(), bar);
    }
    render_content("specialization", kerbal.trait);
    if (Kerbalism.detected_mods.DeepFreeze) render_content("hibernated", kd.disabled > 0 ? "yes" : "no");
    if (Kerbalism.detected_mods.CLS) render_content("inside", v.isEVA ? "EVA" : Lib.Epsilon(kd.space_name.Length == 0 ? v.vesselName : kd.space_name, 24));
    render_space();
  }
Beispiel #6
0
  // return quality-of-life bonus
  public static double Bonus(Vessel v, string k_name)
  {
    // get QoL data from db
    kerbal_data kd = DB.KerbalData(k_name);

    // calculate quality of life bonus
    return Bonus(kd.living_space, kd.entertainment, Lib.Landed(v), Signal.Link(v).linked, Lib.CrewCount(v) < 2u);
  }
Beispiel #7
0
 // hook: DisableKerbal()
 public static void hook_DisableKerbal(string k_name, bool disabled)
 {
     if (!DB.Ready())
     {
         return;
     }
     if (!DB.Kerbals().ContainsKey(k_name))
     {
         return;
     }
     DB.KerbalData(k_name).disabled = disabled ? 1u : 0;
 }
Beispiel #8
0
 // hook: LivingSpace()
 public static double hook_LivingSpace(string k_name)
 {
     if (!DB.Ready())
     {
         return(1.0);
     }
     if (!DB.Kerbals().ContainsKey(k_name))
     {
         return(1.0);
     }
     return(DB.KerbalData(k_name).living_space);
 }
Beispiel #9
0
 // hook: Entertainment()
 public static double hook_Entertainment(string k_name)
 {
     if (!DB.Ready())
     {
         return(1.0);
     }
     if (!DB.Kerbals().ContainsKey(k_name))
     {
         return(1.0);
     }
     return(DB.KerbalData(k_name).entertainment);
 }
Beispiel #10
0
 // hook: Shielding()
 public static double hook_Shielding(string k_name)
 {
     if (!DB.Ready())
     {
         return(0.0);
     }
     if (!DB.Kerbals().ContainsKey(k_name))
     {
         return(0.0);
     }
     return(DB.KerbalData(k_name).shielding);
 }
Beispiel #11
0
        // hook: InjectRadiation()
        public static void hook_InjectRadiation(string k_name, double amount)
        {
            if (!DB.Ready())
            {
                return;
            }
            if (!DB.Kerbals().ContainsKey(k_name))
            {
                return;
            }
            kerbal_data kd = DB.KerbalData(k_name);

            kd.radiation = Math.Max(kd.radiation + amount, 0.0);
        }
Beispiel #12
0
 public override string info()
 {
   kerbal_data kd = DB.KerbalData(crew.name);
   int worst = 0;
   foreach(Rule r in Kerbalism.rules)
   {
     double problem = kd.kmon[r.name].problem;
     if (problem > r.danger_threshold) worst = Math.Max(worst, 2);
     else if (problem > r.warning_threshold) worst = Math.Max(worst, 1);
   }
   switch(worst)
   {
     case 2: return "<color=red>danger</color>";
     case 1: return "<color=yellow>warning</color>";
     default: return "<color=green>healthy</color>";
   }
 }
Beispiel #13
0
        void updateConnectedSpaces(Vessel v, vessel_info vi)
        {
            // get CLS handler
            var cls = CLS.get();

            // calculate whole-space
            if (cls == null)
            {
                double living_space  = QualityOfLife.LivingSpace((uint)vi.crew_count, (uint)vi.crew_capacity);
                double entertainment = QualityOfLife.Entertainment(v);
                double shielding     = Radiation.Shielding(v);

                foreach (var c in v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew())
                {
                    kerbal_data kd = DB.KerbalData(c.name);
                    kd.living_space  = living_space;
                    kd.entertainment = entertainment;
                    kd.shielding     = shielding;
                    kd.space_name    = "";
                }
            }
            // calculate connected-space
            // note: avoid problem at scene change
            else if (cls.Vessel != null && cls.Vessel.Spaces.Count > 0)
            {
                // calculate internal spaces
                foreach (var space in cls.Vessel.Spaces)
                {
                    double living_space  = QualityOfLife.LivingSpace(space);
                    double entertainment = QualityOfLife.Entertainment(v, space);
                    double shielding     = Radiation.Shielding(space);

                    foreach (var c in space.Crew)
                    {
                        kerbal_data kd = DB.KerbalData(c.Kerbal.name);
                        kd.living_space  = living_space;
                        kd.entertainment = entertainment;
                        kd.shielding     = shielding;
                        kd.space_name    = space.Name;
                    }
                }
            }
        }
Beispiel #14
0
        // hook: InjectRadiation()
        public static void hook_InjectRadiation(string k_name, double amount)
        {
            if (!DB.Ready())
            {
                return;
            }
            if (!DB.Kerbals().ContainsKey(k_name))
            {
                return;
            }
            kerbal_data kd = DB.KerbalData(k_name);

            foreach (Rule r in Kerbalism.rules)
            {
                if (r.modifier.Contains("radiation"))
                {
                    var kmon = DB.KmonData(k_name, r.name);
                    kmon.problem = Math.Max(kmon.problem + amount, 0.0);
                }
            }
        }
Beispiel #15
0
  void render_internal_space(Vessel v, vessel_info vi, List<ProtoCrewMember> crew)
  {
    // do not render internal space info for eva vessels
    if (v.isEVA) return;

    // if there is no crew, no space will be found, so do nothing in that case
    if (crew.Count == 0) return;

    // collect set of spaces
    // note: this is guaranteed to get at least a space (because there is at least one crew member)
    List<space_details> spaces = new List<space_details>();
    foreach(var c in crew)
    {
      kerbal_data kd = DB.KerbalData(c.name);
      space_details sd = spaces.Find(k => k.name == kd.space_name);
      if (sd == null)
      {
        sd = new space_details();
        sd.name = kd.space_name;
        sd.living_space = kd.living_space;
        sd.entertainment = kd.entertainment;
        sd.shielding = kd.shielding;
        spaces.Add(sd);
      }
      ++sd.crew_count;
    }

    // select a space
    space_details space = spaces[space_index % spaces.Count];

    // render it
    string radiation_txt = vi.radiation > double.Epsilon
      ? Lib.BuildString(" <i>(", Lib.HumanReadableRadiationRate(vi.radiation * (1.0 - space.shielding)), ")</i>")
      : "";
    render_title(space.name.Length > 0 && spaces.Count > 1 ? Lib.Epsilon(space.name.ToUpper(), 20) : "VESSEL", ref space_index, spaces.Count);
    render_content("living space", QualityOfLife.LivingSpaceToString(space.living_space));
    render_content("entertainment", QualityOfLife.EntertainmentToString(space.entertainment));
    render_content("shielding", Lib.BuildString(Radiation.ShieldingToString(space.shielding), radiation_txt));
    render_space();
  }
Beispiel #16
0
        void manageResqueMission(Vessel v)
        {
            // skip eva dead kerbals
            // rationale: getting the kerbal data will create it again, leading to spurious resque mission detection
            if (EVA.IsDead(v))
            {
                return;
            }

            // 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)
                {
                    // give the vessel some supply
                    Lib.RequestResource(v, v.isEVA ? "EVA Propellant" : "MonoPropellant", -Settings.ResqueMonoPropellant);
                    Lib.RequestResource(v, "ElectricCharge", -Settings.ResqueElectricCharge);
                    Lib.RequestResource(v, "Food", -Settings.ResqueFood);
                    Lib.RequestResource(v, "Oxygen", -Settings.ResqueOxygen);

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

                    // show a message
                    Message.Post("We found <b>" + c.name + "</b>", (c.gender == ProtoCrewMember.Gender.Male ? "He" : "She") + "'s still alive!");
                }
            }
        }
Beispiel #17
0
        // kill a kerbal
        public static void Kill(Vessel v, ProtoCrewMember c)
        {
            // if on pod
            if (!v.isEVA)
            {
                // forget kerbal data
                DB.ForgetKerbal(c.name);

                // if vessel is loaded
                if (v.loaded)
                {
                    // find part
                    Part part = null;
                    foreach (Part p in v.parts)
                    {
                        if (p.protoModuleCrew.Find(k => k.name == c.name) != null)
                        {
                            part = p; break;
                        }
                    }

                    // remove kerbal and kill it
                    part.RemoveCrewmember(c);
                    c.Die();
                }
                // if vessel is not loaded
                else
                {
                    // find proto part
                    ProtoPartSnapshot part = null;
                    foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
                    {
                        if (p.HasCrew(c.name))
                        {
                            part = p; break;
                        }
                    }

                    // remove from vessel
                    part.RemoveCrew(c.name);

                    // flag as dead
                    c.rosterStatus = ProtoCrewMember.RosterStatus.Dead;
                }
            }
            // else it must be an eva death
            else
            {
                // flag as eva death
                DB.KerbalData(c.name).eva_dead = true;

                // rename vessel
                v.vesselName = c.name + "'s body";
            }

            // remove reputation
            if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
            {
                Reputation.Instance.AddReputation(-Settings.DeathReputationPenalty, TransactionReasons.Any);
            }
        }
Beispiel #18
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);
                }
            }
        }
Beispiel #19
0
  // implement life support mechanics
  public void FixedUpdate()
  {
    // avoid case when DB isn't ready for whatever reason
    if (!DB.Ready()) return;

    // do nothing in the editors and the menus
    if (!Lib.SceneIsGame()) return;

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

    // get time elapsed from last update
    double elapsed_s = TimeWarp.fixedDeltaTime;

    // for each vessel
    foreach(Vessel v in FlightGlobals.Vessels)
    {
      // skip invalid vessels
      if (!Lib.IsVessel(v)) continue;

      // skip dead eva kerbals
      if (EVA.IsDead(v)) continue;

      // get crew
      List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

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

      // get temperature difference
      // note: for gameplay reasons, climatization doesn't consume anything landed at home
      bool landed_home = Lib.Landed(v) && v.mainBody == FlightGlobals.GetHomeBody();
      double temp_diff = landed_home ? 0.0 : Math.Abs(info.temperature - Settings.SurvivalTemperature);
      double temp_sign = landed_home ? 1.0 : info.temperature > Settings.SurvivalTemperature ? 1.0 : -1.0;

      // determine if inside breathable atmosphere
      bool breathable = BreathableAtmosphere(v);


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

        // skip resque kerbals
        if (kd.resque == 1) continue;

        // skip disabled kerbals
        if (kd.disabled == 1) continue;

        // consume ec for climate control
        double ec_required = temp_diff * Settings.ElectricChargePerSecond * elapsed_s;
        double ec_consumed = Lib.RequestResource(v, "ElectricCharge", ec_required);
        double ec_perc = ec_required > 0.0 ? ec_consumed / ec_required : 0.0;

        // reset kerbal temperature, if necessary
        if (ec_required <= double.Epsilon || ec_perc >= 1.0 - double.Epsilon)
        {
          kd.temperature = 0.0;
        }
        else
        {
          // degenerate kerbal temperature
          kd.temperature += Settings.TemperatureDegradationRate * elapsed_s * (1.0 - ec_perc) * temp_diff * temp_sign;

          // kill kerbal if necessary
          if (kd.temperature <= -Settings.TemperatureFatalThreshold)
          {
            Message.Post(Severity.fatality, KerbalEvent.climate_low, v, c);
            Kerbalism.Kill(v, c);
          }
          else if (kd.temperature >= Settings.TemperatureFatalThreshold)
          {
            Message.Post(Severity.fatality, KerbalEvent.climate_high, v, c);
            Kerbalism.Kill(v, c);
          }
          // show warnings
          else if (kd.temperature <= -Settings.TemperatureDangerThreshold && kd.msg_freezing < 2)
          {
            Message.Post(Severity.danger, KerbalEvent.climate_low, v, c);
            kd.msg_freezing = 2;
          }
          else if (kd.temperature <= -Settings.TemperatureWarningThreshold && kd.msg_freezing < 1)
          {
            Message.Post(Severity.warning, KerbalEvent.climate_low, v, c);
            kd.msg_freezing = 1;
          }
          else if (kd.temperature > -Settings.TemperatureWarningThreshold && kd.msg_freezing > 0)
          {
            Message.Post(Severity.relax, KerbalEvent.climate_low, v, c);
            kd.msg_freezing = 0;
          }
          else if (kd.temperature >= Settings.TemperatureDangerThreshold && kd.msg_burning < 2)
          {
            Message.Post(Severity.danger, KerbalEvent.climate_high, v, c);
            kd.msg_burning = 2;
          }
          else if (kd.temperature >= Settings.TemperatureWarningThreshold && kd.msg_burning < 1)
          {
            Message.Post(Severity.warning, KerbalEvent.climate_high, v, c);
            kd.msg_burning = 1;
          }
          else if (kd.temperature < Settings.TemperatureWarningThreshold && kd.msg_burning > 0)
          {
            Message.Post(Severity.relax, KerbalEvent.climate_high, v, c);
            kd.msg_burning = 0;
          }
        }


        // if its meal time for this kerbal
        kd.time_since_food += elapsed_s;
        if (kd.time_since_food >= Settings.MealFrequency)
        {
          // consume food
          const double food_required = Settings.FoodPerMeal;
          double food_consumed = Lib.RequestResource(v, "Food", food_required);
          double food_perc = food_consumed / food_required;

          // reset kerbal starvation, if necessary
          if (food_perc >= 1.0 - double.Epsilon)
          {
            kd.starved = 0.0;
            kd.time_since_food = 0.0;
          }
          else
          {
            // assure piecewise consumption
            Lib.RequestResource(v, "Food", -food_consumed);
            food_consumed = 0.0;

            // degenerate kerbal starvation
            kd.starved += Settings.StarvedDegradationRate * elapsed_s;

            // kill kerbal if necessary
            if (kd.starved >= Settings.StarvedFatalThreshold)
            {
              Message.Post(Severity.fatality, KerbalEvent.food, v, c);
              Kerbalism.Kill(v, c);
            }
            // show warnings
            else if (kd.starved >= Settings.StarvedDangerThreshold && kd.msg_starved < 2)
            {
              Message.Post(Severity.danger, KerbalEvent.food, v, c);
              kd.msg_starved = 2;
            }
            else if (kd.starved >= Settings.StarvedWarningThreshold && kd.msg_starved < 1)
            {
              Message.Post(Severity.warning, KerbalEvent.food, v, c);
              kd.msg_starved = 1;
            }
            else if (kd.starved < Settings.StarvedWarningThreshold && kd.msg_starved > 0)
            {
              Message.Post(Severity.relax, KerbalEvent.food, v, c);
              kd.msg_starved = 0;
            }
          }

          // produce waste
          Lib.RequestResource(v, "Waste", -food_consumed);
        }

        // if not inside a breathable atmosphere
        if (!breathable)
        {
          // consume oxygen
          double oxygen_required = Settings.OxygenPerSecond * elapsed_s;
          double oxygen_consumed = Lib.RequestResource(v, "Oxygen", oxygen_required);
          double oxygen_perc = oxygen_consumed / oxygen_required;

          // reset kerbal deprivation, if necessary
          if (oxygen_perc >= 1.0 - double.Epsilon)
          {
            kd.deprived = 0.0;
          }
          else
          {
            // degenerate kerbal deprivation
            kd.deprived += Settings.DeprivedDegradationRate * elapsed_s * (1.0 - oxygen_perc);

            // kill kerbal if necessary
            if (kd.deprived >= Settings.DeprivedFatalThreshold)
            {
              Message.Post(Severity.fatality, KerbalEvent.oxygen, v, c);
              Kerbalism.Kill(v, c);
            }
            // show warnings
            else if (kd.deprived >= Settings.DeprivedDangerThreshold && kd.msg_deprived < 2)
            {
              Message.Post(Severity.danger, KerbalEvent.oxygen, v, c);
              kd.msg_deprived = 2;
            }
            else if (kd.deprived >= Settings.DeprivedWarningThreshold && kd.msg_deprived < 1)
            {
              Message.Post(Severity.warning, KerbalEvent.oxygen, v, c);
              kd.msg_deprived = 1;
            }
            else if (kd.deprived < Settings.DeprivedWarningThreshold && kd.msg_deprived > 0)
            {
              Message.Post(Severity.relax, KerbalEvent.oxygen, v, c);
              kd.msg_deprived = 0;
            }
          }

          // produce CO2
          Lib.RequestResource(v, "CO2", -oxygen_consumed);
        }
      }
    }
  }
Beispiel #20
0
  void render_info()
  {
    // find vessel
    Vessel v = FlightGlobals.Vessels.Find(k => k.id == vessel_id);

    // forget vessel if it doesn't exist anymore, or if its a dead eva kerbal
    if (v == null || EVA.IsDead(v)) { vessel_id = Guid.Empty; return; }

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

    render_title("ENVIRONMENT");
    render_content("Temperature:\t", Lib.HumanReadableTemp(vi.temperature));
    render_content("Radiation:\t", Lib.HumanReadableRadiationRate(vi.env_radiation));
    render_content("Atmosphere:\t", v.mainBody.atmosphere ? " yes" + (vi.breathable ? " <i>(breathable)</i>" : "") : "no");
    render_space();

    // render supplies
    if (Kerbalism.supply_rules.Count > 0 || Kerbalism.ec_rule != null)
    {
      render_title("SUPPLIES");
      if (Kerbalism.ec_rule != null)
      {
        var vmon = vi.vmon[Kerbalism.ec_rule.name];
        render_content(fix_title("Battery:"), vmon.level > double.Epsilon ? Lib.HumanReadableDuration(vmon.depletion) : "none");
      }
      if (Lib.CrewCapacity(v) > 0)
      {
        foreach(Rule r in Kerbalism.supply_rules)
        {
          var vmon = vi.vmon[r.name];
          render_content(fix_title(r.resource_name + ":"), vmon.level > double.Epsilon ? Lib.HumanReadableDuration(vmon.depletion) : "none");
        }
      }
      render_space();
    }


    // get crew
    var crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

    // do not render internal spaces info for eva vessels
    if (!v.isEVA)
    {
      // collect set of spaces
      Dictionary<string, space_details> spaces = new Dictionary<string, space_details>();
      foreach(var c in crew)
      {
        kerbal_data kd = DB.KerbalData(c.name);
        if (!spaces.ContainsKey(kd.space_name))
        {
          space_details sd = new space_details();
          sd.living_space = kd.living_space;
          sd.entertainment = kd.entertainment;
          sd.shielding = kd.shielding;
          spaces.Add(kd.space_name, sd);
        }
        ++(spaces[kd.space_name].crew_count);
      }

      // for each space
      foreach(var space in spaces)
      {
        string space_name = space.Key;
        space_details det = space.Value;

        string radiation_txt = vi.env_radiation > double.Epsilon
          ? " <i>(" + Lib.HumanReadableRadiationRate(vi.env_radiation * (1.0 - det.shielding)) + ")</i>"
          : "";

        render_title(space_name.Length > 0 ? space_name.ToUpper() : v.isEVA ? "EVA" : "VESSEL");
        render_content("Living space:\t", QualityOfLife.LivingSpaceToString(det.living_space));
        render_content("Entertainment:\t", QualityOfLife.EntertainmentToString(det.entertainment));
        render_content("Shielding:\t", Radiation.ShieldingToString(det.shielding) + radiation_txt);
        render_space();
      }
    }

    // for each kerbal
    if (Kerbalism.rules.Count > 0)
    {
      foreach(var c in crew)
      {
        kerbal_data kd = DB.KerbalData(c.name);
        render_title(c.name.ToUpper());
        foreach(var q in Kerbalism.rules)
        {
          Rule r = q.Value;
          if (r.degeneration > double.Epsilon)
          {
            var kmon = DB.KmonData(c.name, r.name);
            var bar = Lib.ProgressBar(23, kmon.problem, r.warning_threshold, r.danger_threshold, r.fatal_threshold, kd.disabled > 0 ? "cyan" : "");
            render_content(fix_title(r.name + ":"), bar);
          }
        }
        if (kd.space_name.Length > 0 && !v.isEVA) render_content("Inside:\t\t", kd.space_name);
        if (kd.disabled > 0) render_content("Hibernated:\t", "yes");
        render_space();
      }
    }

    // for each greenhouse
    var greenhouses = Greenhouse.GetGreenhouses(v);
    foreach(var greenhouse in greenhouses)
    {
      render_title("GREENHOUSE");
      render_content("Lighting:\t\t", (greenhouse.lighting * 100.0).ToString("F0") + "%");
      render_content("Growth:\t\t", (greenhouse.growth * 100.0).ToString("F0") + "%");
      render_content("Harvest:\t\t", Lib.HumanReadableDuration(greenhouse.growing > double.Epsilon ? 1.0 / greenhouse.growing : 0.0));
      render_space();
    }
  }
Beispiel #21
0
  public static void applyRules(Vessel v, vessel_info vi, vessel_data vd, vessel_resources resources, double elapsed_s)
  {
    // get crew
    List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

    // get breathable modifier
    double breathable = vi.breathable ? 0.0 : 1.0;

    // get temp diff modifier
    double temp_diff = v.altitude < 2000.0 && v.mainBody == FlightGlobals.GetHomeBody() ? 0.0 : Sim.TempDiff(vi.temperature);

    // for each rule
    foreach(Rule r in Kerbalism.rules)
    {
      // get resource handler
      resource_info res = r.resource_name.Length > 0 ? resources.Info(v, r.resource_name) : null;

      // if a resource is specified
      if (res != null)
      {
        // get data from db
        vmon_data vmon = DB.VmonData(v.id, r.name);

        // message obey user config
        bool show_msg = (r.resource_name == "ElectricCharge" ? vd.cfg_ec > 0 : vd.cfg_supply > 0);

        // no messages with no capacity
        if (res.capacity > double.Epsilon)
        {
          // manned/probe message variant
          uint variant = crew.Count > 0 ? 0 : 1u;

          // manage messages
          if (res.level <= double.Epsilon && vmon.message < 2)
          {
            if (r.empty_message.Length > 0 && show_msg) Message.Post(Severity.danger, Lib.ExpandMsg(r.empty_message, v, null, variant));
            vmon.message = 2;
          }
          else if (res.level < r.low_threshold && vmon.message < 1)
          {
            if (r.low_message.Length > 0 && show_msg) Message.Post(Severity.warning, Lib.ExpandMsg(r.low_message, v, null, variant));
            vmon.message = 1;
          }
          else if (res.level > r.low_threshold && vmon.message > 0)
          {
            if (r.refill_message.Length > 0 && show_msg) Message.Post(Severity.relax, Lib.ExpandMsg(r.refill_message, v, null, variant));
            vmon.message = 0;
          }
        }
      }

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

        // skip resque kerbals
        if (kd.resque == 1) continue;

        // skip disabled kerbals
        if (kd.disabled == 1) continue;

        // get supply data from db
        kmon_data kmon = DB.KmonData(c.name, r.name);


        // get product of all environment modifiers
        double k = 1.0;
        foreach(string modifier in r.modifier)
        {
          switch(modifier)
          {
            case "breathable":  k *= breathable;                              break;
            case "temperature": k *= temp_diff;                               break;
            case "radiation":   k *= vi.radiation * (1.0 - kd.shielding);     break;
            case "qol":         k /= QualityOfLife.Bonus(kd.living_space, kd.entertainment, vi.landed, vi.link.linked, vi.crew_count == 1); break;
          }
        }


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

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

          // consume time
          kmon.time_since -= step * r.interval;

          // remember if a meal is consumed in this simulation step
          res.meal_consumed |= step > 0.99;
        }


        // if continuous, or if one or more intervals elapsed
        if (step > double.Epsilon)
        {
          // indicate if we must degenerate
          bool must_degenerate = true;

          // if there is a resource specified, and this isn't just a monitoring rule
          if (res != null && r.rate > double.Epsilon)
          {
            // determine amount of resource to consume
            double required = r.rate          // rate per-second or per interval
                            * k               // product of environment modifiers
                            * step;           // seconds elapsed or number of steps

            // if there is no waste
            if (r.waste_name.Length == 0)
            {
              // simply consume (that is faster)
              res.Consume(required);

            }
            // if there is waste
            else
            {
              // transform resource into waste
              resource_recipe recipe = new resource_recipe(resource_recipe.rule_priority);
              recipe.Input(r.resource_name, required);
              recipe.Output(r.waste_name, required * r.waste_ratio);
              resources.Transform(recipe);
            }

            // reset degeneration when consumed, or when not required at all
            // note: evaluating amount from previous simulation step
            if (required <= double.Epsilon || res.amount > double.Epsilon)
            {
              // slowly recover instead of instant reset
              kmon.problem *= 1.0 / (1.0 + Math.Max(r.interval, 1.0) * step * 0.002);
              kmon.problem = Math.Max(kmon.problem, 0.0);

              // do not degenerate
              must_degenerate = false;
            }
          }

          // degenerate if this rule is resource-less, or if there was not enough resource in the vessel
          if (must_degenerate)
          {
            kmon.problem += r.degeneration            // degeneration rate per-second or per-interval
                          * k                         // product of environment modifiers
                          * step                      // seconds elapsed or by number of steps
                          * Variance(c, r.variance);  // kerbal-specific variance
          }


          // determine message variant
          uint variant = vi.temperature < Settings.SurvivalTemperature ? 0 : 1u;

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

            if (r.breakdown)
            {
              Kerbalism.Breakdown(v, c);
              kmon.problem = r.danger_threshold * 1.01; //< move back to danger threshold
            }
            else
            {
              Kerbalism.Kill(v, c);
            }
          }
          // show messages
          else if (kmon.problem >= r.danger_threshold && kmon.message < 2)
          {
            if (r.danger_message.Length > 0) Message.Post(Severity.danger, Lib.ExpandMsg(r.danger_message, v, c, variant));
            kmon.message = 2;
          }
          else if (kmon.problem >= r.warning_threshold && kmon.message < 1)
          {
            if (r.warning_message.Length > 0) Message.Post(Severity.warning, Lib.ExpandMsg(r.warning_message, v, c, variant));
            kmon.message = 1;
          }
          else if (kmon.problem < r.warning_threshold && kmon.message > 0)
          {
            if (r.relax_message.Length > 0) Message.Post(Severity.relax, Lib.ExpandMsg(r.relax_message, v, c, variant));
            kmon.message = 0;
          }
        }
      }
    }
  }
Beispiel #22
0
        // implement radiation mechanics
        public void FixedUpdate()
        {
            // avoid case when DB isn't ready for whatever reason
            if (!DB.Ready())
            {
                return;
            }

            // do nothing in the editors and the menus
            if (!Lib.SceneIsGame())
            {
                return;
            }

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

            // get time elapsed from last update
            double elapsed_s = TimeWarp.fixedDeltaTime;

            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // skip invalid vessels
                if (!Lib.IsVessel(v))
                {
                    continue;
                }

                // skip dead eva kerbals
                if (EVA.IsDead(v))
                {
                    continue;
                }

                // get crew
                List <ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

                // get crew count
                int crew_count = Lib.CrewCount(v);

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

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


                // belt warnings
                // note: we only show it for manned vesssels, but the first time we also show it for probes
                if (crew_count > 0 || DB.NotificationData().first_belt_crossing == 0)
                {
                    if (InsideBelt(v) && vd.msg_belt < 1)
                    {
                        Message.Post("<b>" + v.vesselName + "</b> is crossing <i>" + v.mainBody.bodyName + " radiation belt</i>", "Exposed to extreme radiation");
                        vd.msg_belt = 1;
                        DB.NotificationData().first_belt_crossing = 1; //< record first belt crossing
                    }
                    else if (!InsideBelt(v) && vd.msg_belt > 0)
                    {
                        // no message after crossing the belt
                        vd.msg_belt = 0;
                    }
                }


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

                    // skip resque kerbals
                    if (kd.resque == 1)
                    {
                        continue;
                    }

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

                    // accumulate radiation
                    kd.radiation += info.radiation * elapsed_s;

                    // kill kerbal if necessary
                    if (kd.radiation >= Settings.RadiationFatalThreshold)
                    {
                        Message.Post(Severity.fatality, KerbalEvent.radiation, v, c);
                        Kerbalism.Kill(v, c);
                    }
                    // show warnings
                    else if (kd.radiation >= Settings.RadiationDangerThreshold && kd.msg_radiation < 2)
                    {
                        Message.Post(Severity.danger, KerbalEvent.radiation, v, c);
                        kd.msg_radiation = 2;
                    }
                    else if (kd.radiation >= Settings.RadiationWarningThreshold && kd.msg_radiation < 1)
                    {
                        Message.Post(Severity.danger, KerbalEvent.radiation, v, c);
                        kd.msg_radiation = 1;
                    }
                    // note: no recovery from radiations
                }
            }
        }
Beispiel #23
0
  // implement quality-of-life mechanics
  public void FixedUpdate()
  {
    // avoid case when DB isn't ready for whatever reason
    if (!DB.Ready()) return;

    // do nothing in the editors and the menus
    if (!Lib.SceneIsGame()) return;

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

    // get time elapsed from last update
    double elapsed_s = TimeWarp.fixedDeltaTime;

    // for each vessel
    foreach(Vessel v in FlightGlobals.Vessels)
    {
      // skip invalid vessels
      if (!Lib.IsVessel(v)) continue;

      // skip dead eva kerbals
      if (EVA.IsDead(v)) continue;

      // get crew
      List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

      // calculate quality-of-life bonus
      double qol = Bonus(v);

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

        // skip resque kerbals
        if (kd.resque == 1) continue;

        // skip disabled kerbals
        if (kd.disabled == 1) continue;

        // accumulate stress
        kd.stressed += Settings.StressedDegradationRate * elapsed_s / (qol * Variance(c));

        // in case of breakdown
        if (kd.stressed >= Settings.StressedEventThreshold)
        {
          // trigger breakdown event
          Breakdown(v, c);

          // reset stress halfway between danger and event threshold
          kd.stressed = (Settings.StressedDangerThreshold + Settings.StressedEventThreshold) * 0.5;
        }
        // show warning messages
        else if (kd.stressed >= Settings.StressedDangerThreshold && kd.msg_stressed < 2)
        {
          Message.Post(Severity.danger, KerbalEvent.stress, v, c);
          kd.msg_stressed = 2;
        }
        else if (kd.stressed >= Settings.StressedWarningThreshold && kd.msg_stressed < 1)
        {
          Message.Post(Severity.warning, KerbalEvent.stress, v, c);
          kd.msg_stressed = 1;
        }
        // note: no recovery from stress
      }
    }
  }
Beispiel #24
0
 public static kerbal_data KerbalData(Vessel v)
 {
   var crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();
   return DB.KerbalData(crew[0].name);
 }