Exemple #1
0
 void render_qol(qol_data qol)
 {
   render_title("QUALITY OF LIFE");
   render_content("living space", QualityOfLife.LivingSpaceToString(qol.living_space));
   render_content("entertainment", QualityOfLife.EntertainmentToString(qol.entertainment));
   render_content("other factors", qol.factors);
   render_content("time to instability", Lib.HumanReadableDuration(qol.time_to_instability));
   render_space();
 }
Exemple #2
0
  public static qol_data analyze_qol(List<Part> parts, environment_data env, crew_data crew, signal_data signal)
  {
    // store data
    qol_data qol = new qol_data();

    // scan the parts
    foreach(Part p in parts)
    {
      // for each module
      foreach(PartModule m in p.Modules)
      {
        // entertainment
        if (m.moduleName == "Entertainment")
        {
          Entertainment mm = (Entertainment)m;
          qol.entertainment *= mm.rate;
        }
      }
    }

    // calculate Quality-Of-Life bonus
    // note: ignore kerbal-specific variance
    if (crew.capacity > 0)
    {
      double bonus = QualityOfLife.Bonus(crew.count, crew.capacity, qol.entertainment, env.landed, signal.range > 0.0);
      qol.living_space = QualityOfLife.LivingSpace(crew.count, crew.capacity);
      qol.time_to_instability = bonus / Settings.StressedDegradationRate;
      List<string> factors = new List<string>();
      if (crew.count > 1) factors.Add("not-alone");
      if (signal.range > 0.0) factors.Add("call-home");
      if (env.landed) factors.Add("firm-ground");
      if (factors.Count == 0) factors.Add("none");
      qol.factors = String.Join(", ", factors.ToArray());
    }
    else
    {
      qol.living_space = 0.0;
      qol.time_to_instability = double.NaN;
      qol.factors = "none";
    }

    // return data
    return qol;
  }
Exemple #3
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;
                    }
                }
            }
        }
Exemple #4
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();
  }
Exemple #5
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;
          }
        }
      }
    }
  }
Exemple #6
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();
    }
  }