Example #1
0
 //  constructor
 public Kerbalism()
 {
     // enable global access
     Fetch = this;
     Communications.NetworkInitialized = false;
     RemoteTech.NetworkInitialized     = false;
 }
Example #2
0
        //  constructor
        public Kerbalism()
        {
            // enable global access
            Fetch = this;
            Communications.NetworkInitialized  = false;
            Communications.NetworkInitializing = false;

            SerenityEnabled = Expansions.ExpansionsLoader.IsExpansionInstalled("Serenity");
        }
Example #3
0
 private void OnDestroy()
 {
     Fetch = null;
 }
Example #4
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
                }
            }
        }
Example #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;
          }
        }
      }
    }
  }
Example #6
0
  // called every simulation step
  void FixedUpdate()
  {
    // do nothing if paused
    if (Lib.IsPaused()) return;

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

    // do nothing if db isn't ready
    if (!DB.Ready()) return;

    // get elapsed time
    double elapsed_s = Kerbalism.elapsed_s;

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

      // skip invalid vessels
      if (!vi.is_valid) continue;

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

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

        // consume relay EC and show signal warnings
        signal.update(v, vi, vd, resources, elapsed_s * vi.time_dilation);

        // apply rules
        Rule.applyRules(v, vi, vd, resources, elapsed_s * vi.time_dilation);

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

        // update computer
        vd.computer.update(v, elapsed_s);

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

      // decay unloaded vessels inside atmosphere
      Kerbalism.atmosphereDecay(last_v, last_vi, last_time);

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

      // consume relay EC and show signal warnings
      signal.update(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation);

      // apply rules
      Rule.applyRules(last_v, last_vi, last_vd, last_resources, last_time * last_vi.time_dilation);

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

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

      // update computer
      last_vd.computer.update(last_v, last_time);

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


    // update storm data for one body per-step
    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;
  }
Example #7
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);
        }
      }
    }
  }