示例#1
0
 // return max malfunction count among all parts of a vessel
 public static uint MaxMalfunction(Vessel v)
 {
   uint max_malfunction = 0;
   if (v.loaded)
   {
     foreach(Malfunction m in v.FindPartModulesImplementing<Malfunction>())
     {
       max_malfunction = Math.Max(max_malfunction, m.malfunctions);
     }
   }
   else
   {
     foreach(ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot m in p.modules)
       {
         if (m.moduleName == "Malfunction")
         {
           max_malfunction = Math.Max(max_malfunction, Lib.GetProtoValue<uint>(m, "malfunctions"));
         }
       }
     }
   }
   return max_malfunction;
 }
示例#2
0
 // return average component quality
 public static double AverageQuality(Vessel v)
 {
   double quality_sum = 0.0;
   double quality_count = 0.0;
   if (v.loaded)
   {
     foreach(Malfunction m in v.FindPartModulesImplementing<Malfunction>())
     {
       quality_sum += m.quality;
       quality_count += 1.0;
     }
   }
   else
   {
     foreach(ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot m in p.modules)
       {
         if (m.moduleName == "Malfunction")
         {
           quality_sum += Lib.GetProtoValue<double>(m, "quality");
           quality_count += 1.0;
         }
       }
     }
   }
   return quality_count > 0.0 ? quality_sum / quality_count : 0.0;
 }
示例#3
0
 // return entertainment on a vessel
 public static double Entertainment(Vessel v)
 {
   // deduce entertainment bonus, multiplying all entertainment factors
   double entertainment = 1.0;
   if (v.loaded)
   {
     foreach(Entertainment m in v.FindPartModulesImplementing<Entertainment>())
     {
       entertainment *= m.rate;
     }
     foreach(GravityRing m in v.FindPartModulesImplementing<GravityRing>())
     {
       entertainment *= m.rate;
     }
   }
   else
   {
     foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot m in part.modules)
       {
         if (m.moduleName == "Entertainment") entertainment *= Lib.GetProtoValue<double>(m, "rate");
         else if (m.moduleName == "GravityRing") entertainment *= Lib.GetProtoValue<double>(m, "rate");
       }
     }
   }
   return entertainment;
 }
示例#4
0
  // return quality-of-life bonus
  public static double Bonus(Vessel v)
  {
    // deduce crew count and capacity
    int crew_count = Lib.CrewCount(v);
    int crew_capacity = Lib.CrewCapacity(v);

    // deduce entertainment bonus, multiplying all entertainment factors
    double entertainment = 1.0;
    if (v.loaded)
    {
      foreach(Entertainment m in v.FindPartModulesImplementing<Entertainment>())
      {
        entertainment *= m.rate;
      }
    }
    else
    {
      foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
      {
        foreach(ProtoPartModuleSnapshot m in part.modules)
        {
          if (m.moduleName == "Entertainment") entertainment *= Lib.GetProtoValue<double>(m, "rate");
        }
      }
    }

    // calculate quality of life bonus
    return Bonus((uint)crew_count, (uint)crew_capacity, entertainment, Lib.Landed(v), Signal.Link(v).linked);
  }
示例#5
0
  // return read-only list of recyclers in a vessel
  public static List<Recycler> GetRecyclers(Vessel v, string resource_name="")
  {
    if (v.loaded)
    {
      var ret = v.FindPartModulesImplementing<Recycler>();
      if (resource_name.Length > 0) ret = ret.FindAll(k => k.resource_name == resource_name);
      return ret == null ? new List<Recycler>() : ret;
    }
    else
    {
      List<Recycler> ret = new List<Recycler>();
      foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
      {
        foreach(ProtoPartModuleSnapshot module in part.modules)
        {
          if (module.moduleName == "Recycler")
          {
            Recycler recycler = new Recycler();
            recycler.is_enabled = Lib.GetProtoValue<bool>(module, "is_enabled");
            recycler.resource_name = Lib.GetProtoValue<string>(module, "resource_name");
            recycler.waste_name = Lib.GetProtoValue<string>(module, "waste_name");
            recycler.ec_rate = Lib.GetProtoValue<double>(module, "ec_rate");
            recycler.waste_rate = Lib.GetProtoValue<double>(module, "waste_rate");
            recycler.waste_ratio = Lib.GetProtoValue<double>(module, "waste_ratio");
            recycler.display_name = Lib.GetProtoValue<string>(module, "display_name");
            recycler.filter_name = Lib.GetProtoValue<string>(module, "filter_name");
            recycler.filter_rate = Lib.GetProtoValue<double>(module, "filter_rate");

            if (resource_name.Length == 0 || recycler.resource_name == resource_name) ret.Add(recycler);
          }
        }
      }
      return ret;
    }
  }
示例#6
0
        void vesselRecovered(ProtoVessel vessel, bool b)
        {
            // note: this is called multiple times when a vessel is recovered, but its safe

            // find out if this was an EVA kerbal and if it was dead
            bool is_eva_dead = false;

            foreach (ProtoPartSnapshot p in vessel.protoPartSnapshots)
            {
                foreach (ProtoPartModuleSnapshot m in p.modules)
                {
                    is_eva_dead |= (m.moduleName == "EVA" && Lib.GetProtoValue <bool>(m, "is_dead"));
                }
            }

            // set roster status of eva dead kerbals
            if (is_eva_dead)
            {
                vessel.GetVesselCrew()[0].rosterStatus = ProtoCrewMember.RosterStatus.Dead;
            }
            // forget kerbal data of recovered kerbals
            else
            {
                foreach (ProtoCrewMember c in vessel.GetVesselCrew())
                {
                    DB.ForgetKerbal(c.name);
                }
            }

            // forget vessel data
            DB.ForgetVessel(vessel.vesselID);
        }
示例#7
0
 // return max malfunction count among all parts of a vessel, or all parts containing the specified module
 public static uint MaxMalfunction(Vessel v, string module_name = "")
 {
   uint max_malfunction = 0;
   if (v.loaded)
   {
     foreach(Malfunction m in v.FindPartModulesImplementing<Malfunction>())
     {
       if (module_name.Length == 0 || m.part.Modules.Contains(module_name))
       {
         max_malfunction = Math.Max(max_malfunction, m.malfunctions);
       }
     }
   }
   else
   {
     foreach(ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot m in p.modules)
       {
         if (m.moduleName == "Malfunction")
         {
           if (module_name.Length == 0 || p.modules.Find(k => k.moduleName == module_name) != null)
           {
             max_malfunction = Math.Max(max_malfunction, Lib.GetProtoValue<uint>(m, "malfunctions"));
           }
         }
       }
     }
   }
   return max_malfunction;
 }
示例#8
0
 // return read-only list of scrubbers in a vessel
 public static List<Scrubber> GetScrubbers(Vessel v, string resource_name="")
 {
   if (v.loaded)
   {
     var ret = v.FindPartModulesImplementing<Scrubber>();
     if (resource_name.Length > 0) ret = ret.FindAll(k => k.resource_name == resource_name);
     return ret == null ? new List<Scrubber>() : ret;
   }
   else
   {
     List<Scrubber> ret = new List<Scrubber>();
     foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot module in part.modules)
       {
         if (module.moduleName == "Scrubber")
         {
           Scrubber scrubber = new Scrubber();
           scrubber.is_enabled = Lib.GetProtoValue<bool>(module, "is_enabled");
           scrubber.ec_rate = Lib.GetProtoValue<double>(module, "ec_rate");
           scrubber.co2_rate = Lib.GetProtoValue<double>(module, "co2_rate");
           scrubber.efficiency = Lib.GetProtoValue<double>(module, "efficiency");
           scrubber.intake_rate = Lib.GetProtoValue(module, "intake_rate", 1.0); //< support versions before 0.9.9.5
           scrubber.resource_name = Lib.GetProtoValue(module, "resource_name", "Oxygen"); //< support versions before 0.9.9.5
           scrubber.waste_name = Lib.GetProtoValue(module, "waste_name", "CO2"); //< support versions before 0.9.9.5
           if (resource_name.Length == 0 || scrubber.resource_name == resource_name) ret.Add(scrubber);
         }
       }
     }
     return ret;
   }
 }
示例#9
0
  // implement malfunction mechanics for unloaded vessels
  public static void BackgroundUpdate(Vessel vessel, uint flight_id)
  {
    // get data
    ProtoPartModuleSnapshot m = Lib.GetProtoModule(vessel, flight_id, "Malfunction");
    uint malfunctions = Lib.GetProtoValue<uint>(m, "malfunctions");
    double min_lifetime = Lib.GetProtoValue<double>(m, "min_lifetime");
    double max_lifetime = Lib.GetProtoValue<double>(m, "max_lifetime");
    double lifetime = Lib.GetProtoValue<double>(m, "lifetime");
    double age = Lib.GetProtoValue<double>(m, "age");
    double quality = Lib.GetProtoValue<double>(m, "quality");
    string malfunction_msg = m.moduleValues.GetValue("malfunction_msg");

    // if for some reason quality wasn't set, default to 1.0 (but don't save it)
    // note: for example, resque vessels failure get background update without prelaunch
    if (quality <= double.Epsilon) quality = 1.0;

    // generate lifetime if necessary
    if (lifetime <= double.Epsilon)
    {
      lifetime = min_lifetime + (max_lifetime - min_lifetime) * Lib.RandomDouble();
    }

    // accumulate age
    age += TimeWarp.fixedDeltaTime
         * AgingCurve(age, min_lifetime, max_lifetime)
         / quality;

    // save data
    // note: done before checking for malfunction because proto Break change data again
    Lib.SetProtoValue<double>(m, "lifetime", lifetime);
    Lib.SetProtoValue<double>(m, "age", age);

    // check age and malfunction if needed
    if (age > lifetime) Break(vessel, m);
  }
示例#10
0
  // trigger malfunction for unloaded module
  public static void Break(Vessel v, ProtoPartModuleSnapshot m)
  {
    // get data
    uint malfunctions = Lib.GetProtoValue<uint>(m, "malfunctions");
    double lifetime = Lib.GetProtoValue<double>(m, "lifetime");
    double age = Lib.GetProtoValue<double>(m, "age");
    string malfunction_msg = m.moduleValues.GetValue("malfunction_msg");

    // limit number of malfunctions per-component
    if (malfunctions >= 2u) return;

    // increase malfunction
    ++malfunctions;

    // reset age and lifetime
    age = 0.0;
    lifetime = 0.0;

    // show message
    if (DB.Ready() && DB.VesselData(v.id).cfg_malfunction == 1)
    {
      Message.Post(Severity.warning, PrepareMsg(malfunction_msg, v, malfunctions));
    }

    // record first malfunction
    if (DB.Ready()) DB.NotificationData().first_malfunction = 1;

    // save data
    Lib.SetProtoValue<uint>(m, "malfunctions", malfunctions);
    Lib.SetProtoValue<double>(m, "lifetime", lifetime);
    Lib.SetProtoValue<double>(m, "age", age);
  }
示例#11
0
  // implement gravity ring mechanics for unloaded vessels
  public static void BackgroundUpdate(Vessel vessel, uint flight_id)
  {
    // get time elapsed from last update
    double elapsed_s = TimeWarp.fixedDeltaTime;

    // get data
    ProtoPartModuleSnapshot m = Lib.GetProtoModule(vessel, flight_id, "GravityRing");
    double entertainment_rate = Lib.GetProtoValue<double>(m, "entertainment_rate");
    double ec_rate = Lib.GetProtoValue<double>(m, "ec_rate");
    float speed = Lib.GetProtoValue<float>(m, "speed");

    // consume ec
    double ec_light_perc = 0.0;
    if (speed > float.Epsilon)
    {
      double ec_light_required = ec_rate * elapsed_s * speed;
      double ec_light = Lib.RequestResource(vessel, "ElectricCharge", ec_light_required);
      ec_light_perc = ec_light / ec_light_required;

      // if there isn't enough ec
      if (ec_light <= double.Epsilon)
      {
        // reset speed
        speed = 0.0f;
      }
    }

    // set entertainment
    double rate = 1.0 + (entertainment_rate - 1.0) * speed;

    // write back data
    Lib.SetProtoValue(m, "speed", speed);
    Lib.SetProtoValue(m, "rate", rate);
  }
示例#12
0
  // implement scrubber mechanics for unloaded vessels
  public static void BackgroundUpdate(Vessel vessel, uint flight_id)
  {
    // get data
    ProtoPartModuleSnapshot m = Lib.GetProtoModule(vessel, flight_id, "Scrubber");
    bool is_enabled = Lib.GetProtoValue<bool>(m, "is_enabled");
    double ec_rate = Lib.GetProtoValue<double>(m, "ec_rate");
    double co2_rate = Lib.GetProtoValue<double>(m, "co2_rate");
    double efficiency = Lib.GetProtoValue<double>(m, "efficiency");

    // if for some reason efficiency wasn't set, default to 50%
    // note: for example, resque vessels scrubbers get background update without prelaunch
    if (efficiency <= double.Epsilon) efficiency = 0.5;

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

    // if inside breathable atmosphere
    if (LifeSupport.BreathableAtmosphere(vessel))
    {
      // produce oxygen from the intake
      Lib.RequestResource(vessel, "Oxygen", -Settings.IntakeOxygenRate * elapsed_s);
    }
    // if outside breathable atmosphere and enabled
    else if (is_enabled)
    {
      // recycle CO2+EC into oxygen
      double co2_required = co2_rate * elapsed_s;
      double co2 = Lib.RequestResource(vessel, "CO2", co2_required);
      double ec_required = ec_rate * elapsed_s * (co2 / co2_required);
      double ec = Lib.RequestResource(vessel, "ElectricCharge", ec_required);
      Lib.RequestResource(vessel, "Oxygen", -co2 * efficiency);
    }
  }
示例#13
0
 // return read-only list of greenhouses in a vessel
 public static List<Greenhouse> GetGreenhouses(Vessel v)
 {
   if (v.loaded) return v.FindPartModulesImplementing<Greenhouse>();
   else
   {
     List<Greenhouse> ret = new List<Greenhouse>();
     foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot module in part.modules)
       {
         if (module.moduleName == "Greenhouse")
         {
           Greenhouse greenhouse = new Greenhouse();
           greenhouse.ec_rate = Lib.GetProtoValue<double>(module, "ec_rate");
           greenhouse.waste_rate = Lib.GetProtoValue<double>(module, "waste_rate");
           greenhouse.harvest_size = Lib.GetProtoValue<double>(module, "harvest_size");
           greenhouse.growth_rate = Lib.GetProtoValue<double>(module, "growth_rate");
           greenhouse.door_opened = Lib.GetProtoValue<bool>(module, "door_opened");
           greenhouse.growth = Lib.GetProtoValue<double>(module, "growth");
           greenhouse.lamps = Lib.GetProtoValue<float>(module, "lamps");
           greenhouse.lighting = Lib.GetProtoValue<double>(module, "lighting");
           ret.Add(greenhouse);
         }
       }
     }
     return ret;
   }
 }
示例#14
0
  // fill antennas data
  void BuildAntennas()
  {
    // get error-correcting code factor
    double ecc = ECC();

    // forget previous antennas
    antennas.Clear();

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

      // store best antenna values
      double best_range = 0.0;
      double best_relay_range = 0.0;
      double best_relay_cost = 0.0;

      // get ec available
      double ec_amount = Lib.GetResourceAmount(v, "ElectricCharge");

      // if the vessel is loaded
      if (v.loaded)
      {
        // choose the best antenna
        foreach(Antenna a in v.FindPartModulesImplementing<Antenna>())
        {
          double range = Range(a.scope, a.penalty, ecc);
          best_range = Math.Max(best_range, range);
          if (a.relay && range > best_relay_range && ec_amount >= a.relay_cost * TimeWarp.deltaTime)
          { best_relay_range = range; best_relay_cost = a.relay_cost; }
        }
      }
      // if the vessel isn't loaded
      else
      {
        // choose the best antenna
        foreach(ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
        {
          foreach(ProtoPartModuleSnapshot m in p.modules)
          {
            if (m.moduleName == "Antenna")
            {
              double range = Range(m.moduleValues.GetValue("scope"), Malfunction.Penalty(p), ecc);
              double relay_cost = Lib.GetProtoValue<double>(m, "relay_cost");
              bool relay = Lib.GetProtoValue<bool>(m, "relay");
              best_range = Math.Max(best_range, range);
              if (relay && range > best_relay_range && ec_amount >= relay_cost * TimeWarp.deltaTime)
              { best_relay_range = range; best_relay_cost = relay_cost; }
            }
          }
        }
      }

      // add antenna data
      antennas.Add(v.id, new antenna_data(Lib.VesselPosition(v), best_range, best_relay_range, best_relay_cost));
    }
  }
示例#15
0
  // return malfunction penalty of a part
  public static double Penalty(ProtoPartSnapshot p, double scale = 0.5)
  {
    // get the module
    // note: if the part has no malfunction, default to no penality
    ProtoPartModuleSnapshot m = p.modules.Find(k => k.moduleName == "Malfunction");
    if (m == null) return 1.0;

    // return penalty;
    uint malfunctions = Lib.GetProtoValue<uint>(m, "malfunctions");
    return Math.Pow(scale, (double)malfunctions);
  }
示例#16
0
 // return true if a vessel is a dead EVA kerbal
 public static bool IsDead(Vessel vessel)
 {
   if (!vessel.isEVA) return false;
   if (vessel.loaded) return vessel.FindPartModulesImplementing<EVA>()[0].is_dead;
   foreach(ProtoPartSnapshot part in vessel.protoVessel.protoPartSnapshots)
   {
     foreach(ProtoPartModuleSnapshot module in part.modules)
     {
       if (module.moduleName == "EVA") return Lib.GetProtoValue<bool>(module, "is_dead");
     }
   }
   return false;
 }
示例#17
0
 // return true if a vessel is a dead EVA kerbal
 public static bool IsDead(Vessel vessel)
 {
   if (!vessel.isEVA) return false;
   if (vessel.loaded)
   {
     var eva = vessel.FindPartModulesImplementing<EVA>();
     if (eva == null || eva.Count == 0) return false; //< assume alive for resque EVA, that don't have our module
     return eva[0].is_dead;
   }
   foreach(ProtoPartSnapshot part in vessel.protoVessel.protoPartSnapshots)
   {
     foreach(ProtoPartModuleSnapshot module in part.modules)
     {
       if (module.moduleName == "EVA") return Lib.GetProtoValue<bool>(module, "is_dead");
     }
   }
   return false;
 }
示例#18
0
 // return read-only list of greenhouses in a vessel
 public static List<Greenhouse> GetGreenhouses(Vessel v, string resource_name="")
 {
   if (v.loaded)
   {
     var ret = v.FindPartModulesImplementing<Greenhouse>();
     if (resource_name.Length > 0) ret = ret.FindAll(k => k.resource_name == resource_name);
     return ret == null ? new List<Greenhouse>() : ret;
   }
   else
   {
     List<Greenhouse> ret = new List<Greenhouse>();
     foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot module in part.modules)
       {
         if (module.moduleName == "Greenhouse")
         {
           Greenhouse greenhouse = new Greenhouse();
           greenhouse.resource_name = Lib.GetProtoValue(module, "resource_name", "Food");  //< support versions before 0.9.9.5
           greenhouse.waste_name = Lib.GetProtoValue(module, "waste_name", "Crap");  //< support versions before 0.9.9.5
           greenhouse.input_name = Lib.GetProtoValue(module, "input_name", "");  //< from version 0.9.9.8
           greenhouse.ec_rate = Lib.GetProtoValue<double>(module, "ec_rate");
           greenhouse.waste_rate = Lib.GetProtoValue<double>(module, "waste_rate");
           greenhouse.input_rate = Lib.GetProtoValue<double>(module, "input_rate", 0.0);
           greenhouse.harvest_size = Lib.GetProtoValue<double>(module, "harvest_size");
           greenhouse.growth_rate = Lib.GetProtoValue<double>(module, "growth_rate");
           greenhouse.waste_bonus = Lib.GetProtoValue(module, "waste_bonus", 0.2); //< support versions before 0.9.9.5
           greenhouse.soil_bonus = Lib.GetProtoValue(module, "soil_bonus", 0.5);  //< support versions before 0.9.9.5
           greenhouse.door_opened = Lib.GetProtoValue<bool>(module, "door_opened");
           greenhouse.growth = Lib.GetProtoValue<double>(module, "growth");
           greenhouse.lamps = Lib.GetProtoValue<float>(module, "lamps");
           greenhouse.lighting = Lib.GetProtoValue<double>(module, "lighting");
           greenhouse.growing = Lib.GetProtoValue<double>(module, "growing");
           if (resource_name.Length == 0 || greenhouse.resource_name == resource_name) ret.Add(greenhouse);
         }
       }
     }
     return ret;
   }
 }
示例#19
0
  // implement recycler mechanics for unloaded vessels
  public static void BackgroundUpdate(Vessel vessel, uint flight_id)
  {
    // get data
    ProtoPartModuleSnapshot m = Lib.GetProtoModule(vessel, flight_id, "Recycler");
    bool is_enabled = Lib.GetProtoValue<bool>(m, "is_enabled");
    string resource_name = Lib.GetProtoValue<string>(m, "resource_name");
    string waste_name = Lib.GetProtoValue<string>(m, "waste_name");
    double ec_rate = Lib.GetProtoValue<double>(m, "ec_rate");
    double waste_rate = Lib.GetProtoValue<double>(m, "waste_rate");
    double waste_ratio = Lib.GetProtoValue<double>(m, "waste_ratio");
    string filter_name = Lib.GetProtoValue<string>(m, "filter_name");
    double filter_rate = Lib.GetProtoValue<double>(m, "filter_rate");

    if (is_enabled)
    {
      // calculate worst required resource percentual
      double worst_input = 1.0;
      double waste_required = waste_rate * TimeWarp.fixedDeltaTime;
      double waste_amount = Lib.GetResourceAmount(vessel, waste_name);
      worst_input = Math.Min(worst_input, waste_amount / waste_required);
      double ec_required = ec_rate * TimeWarp.fixedDeltaTime;
      double ec_amount = Lib.GetResourceAmount(vessel, "ElectricCharge");
      worst_input = Math.Min(worst_input, ec_amount / ec_required);
      double filter_required = filter_rate * TimeWarp.fixedDeltaTime;
      if (filter_name.Length > 0 && filter_rate > 0.0)
      {
        double filter_amount = Lib.GetResourceAmount(vessel, filter_name);
        worst_input = Math.Min(worst_input, filter_amount / filter_required);
      }

      // recycle EC+waste+filter into resource
      Lib.RequestResource(vessel, waste_name, waste_required * worst_input);
      Lib.RequestResource(vessel, "ElectricCharge", ec_required * worst_input);
      Lib.RequestResource(vessel, filter_name, filter_required * worst_input);
      Lib.RequestResource(vessel, resource_name, -waste_required * worst_input * waste_ratio);
    }
  }
示例#20
0
 // return read-only list of scrubbers in a vessel
 public static List<Scrubber> GetScrubbers(Vessel v)
 {
   if (v.loaded) return v.FindPartModulesImplementing<Scrubber>();
   else
   {
     List<Scrubber> ret = new List<Scrubber>();
     foreach(ProtoPartSnapshot part in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot module in part.modules)
       {
         if (module.moduleName == "Scrubber")
         {
           Scrubber scrubber = new Scrubber();
           scrubber.is_enabled = Lib.GetProtoValue<bool>(module, "is_enabled");
           scrubber.ec_rate = Lib.GetProtoValue<double>(module, "ec_rate");
           scrubber.co2_rate = Lib.GetProtoValue<double>(module, "co2_rate");
           scrubber.efficiency = Lib.GetProtoValue<double>(module, "efficiency");
           ret.Add(scrubber);
         }
       }
     }
     return ret;
   }
 }
示例#21
0
 // return true if it make sense to trigger a malfunction on the vessel
 public static bool CanMalfunction(Vessel v)
 {
   if (v.loaded)
   {
     foreach(Malfunction m in v.FindPartModulesImplementing<Malfunction>())
     {
       if (m.malfunctions < 2u) return true;
     }
   }
   else
   {
     foreach(ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
     {
       foreach(ProtoPartModuleSnapshot m in p.modules)
       {
         if (m.moduleName == "Malfunction")
         {
           if (Lib.GetProtoValue<uint>(m, "malfunctions") < 2u) return true;
         }
       }
     }
   }
   return false;
 }
示例#22
0
  // called at every simulation step
  public void FixedUpdate()
  {
    // do nothing if paused
    if (Lib.IsPaused()) return;

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

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

      // skip loaded vessels
      if (vessel.loaded) continue;

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

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

      // calculate atmospheric factor (proportion of flux not blocked by atmosphere)
      double atmo_factor = Sim.AtmosphereFactor(vessel.mainBody, info.position, info.sun_dir);

      // for each part
      foreach(ProtoPartSnapshot part in vessel.protoVessel.protoPartSnapshots)
      {
        // get part prefab (required for module properties)
        Part part_prefab = PartLoader.getPartInfoByName(part.partName).partPrefab;

        // store index of ModuleResourceConverter to process
        // rationale: a part can contain multiple resource converters
        int converter_index = 0;

        // for each module
        foreach(ProtoPartModuleSnapshot module in part.modules)
        {
          // something weird is going on, skip this
          if (!part_prefab.Modules.Contains(module.moduleName)) continue;

          // command module
          if (module.moduleName == "ModuleCommand")
          {
            // get module from prefab
            ModuleCommand command = part_prefab.Modules.GetModules<ModuleCommand>()[0];

            // do not consume if this is a MCM with no crew
            // rationale: for consistency, the game doesn't consume resources for MCM without crew in loaded vessels
            //            this make some sense: you left a vessel with some battery and nobody on board, you expect it to not consume EC
            if (command.minimumCrew == 0 || part.protoModuleCrew.Count > 0)
            {
              // for each input resource
              foreach(ModuleResource ir in command.inputResources)
              {
                // consume the resource
                Lib.RequestResource(vessel, ir.name, ir.rate * TimeWarp.fixedDeltaTime);
              }
            }
          }
          // solar panel
          else if (module.moduleName == "ModuleDeployableSolarPanel")
          {
            // determine if extended
            bool extended = module.moduleValues.GetValue("stateString") == ModuleDeployableSolarPanel.panelStates.EXTENDED.ToString();

            // if in sunlight and extended
            if (info.sunlight && extended)
            {
              // get module from prefab
              ModuleDeployableSolarPanel panel = part_prefab.Modules.GetModules<ModuleDeployableSolarPanel>()[0];

              // produce electric charge
              Lib.RequestResource(vessel, "ElectricCharge", -PanelOutput(vessel, part, panel, info.sun_dir, info.sun_dist, atmo_factor) * TimeWarp.fixedDeltaTime * Malfunction.Penalty(part));
            }
          }
          // generator
          // note: assume generators require all input
          else if (module.moduleName == "ModuleGenerator")
          {
            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("generatorIsActive"));

            // if active
            if (activated)
            {
              // get module from prefab
              ModuleGenerator generator = part_prefab.Modules.GetModules<ModuleGenerator>()[0];

              // determine if vessel is full of all output resources
              bool full = true;
              foreach(var or in generator.outputList)
              {
                double amount = Lib.GetResourceAmount(vessel, or.name);
                double capacity = Lib.GetResourceCapacity(vessel, or.name);
                double perc = capacity > 0.0 ? amount / capacity : 0.0;
                full &= (perc >= 1.0 - double.Epsilon);
              }

              // if not full
              if (!full)
              {
                // calculate worst required resource percentual
                double worst_input = 1.0;
                foreach(var ir in generator.inputList)
                {
                  double required = ir.rate * TimeWarp.fixedDeltaTime;
                  double amount = Lib.GetResourceAmount(vessel, ir.name);
                  worst_input = Math.Min(worst_input, amount / required);
                }

                // for each input resource
                foreach(var ir in generator.inputList)
                {
                  // consume the resource
                  Lib.RequestResource(vessel, ir.name, ir.rate * worst_input * TimeWarp.fixedDeltaTime);
                }

                // for each output resource
                foreach(var or in generator.outputList)
                {
                  // produce the resource
                  Lib.RequestResource(vessel, or.name, -or.rate * worst_input * TimeWarp.fixedDeltaTime * Malfunction.Penalty(part));
                }
              }
            }
          }
          // converter
          // note: support multiple resource converters
          // note: ignore stock temperature mechanic of converters
          // note: ignore autoshutdown
          // note: ignore crew experience bonus (seem that stock ignore it too)
          // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)
          // note: support PlanetaryBaseSystem converters
          // note: support NearFuture reactors
          else if (module.moduleName == "ModuleResourceConverter" || module.moduleName == "ModuleKPBSConverter" || module.moduleName == "FissionReactor")
          {
            // get module from prefab
            ModuleResourceConverter converter = part_prefab.Modules.GetModules<ModuleResourceConverter>()[converter_index++];

            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              // determine if vessel is full of all output resources
              bool full = true;
              foreach(var or in converter.outputList)
              {
                double amount = Lib.GetResourceAmount(vessel, or.ResourceName);
                double capacity = Lib.GetResourceCapacity(vessel, or.ResourceName);
                double perc = capacity > 0.0 ? amount / capacity : 0.0;
                full &= (perc >= converter.FillAmount - double.Epsilon);
              }

              // if not full
              if (!full)
              {
                // calculate worst required resource percentual
                double worst_input = 1.0;
                foreach(var ir in converter.inputList)
                {
                  double required = ir.Ratio * TimeWarp.fixedDeltaTime;
                  double amount = Lib.GetResourceAmount(vessel, ir.ResourceName);
                  worst_input = Math.Min(worst_input, amount / required);
                }

                // for each input resource
                foreach(var ir in converter.inputList)
                {
                  // consume the resource
                  Lib.RequestResource(vessel, ir.ResourceName, ir.Ratio * worst_input * TimeWarp.fixedDeltaTime);
                }

                // for each output resource
                foreach(var or in converter.outputList)
                {
                  // produce the resource
                  Lib.RequestResource(vessel, or.ResourceName, -or.Ratio * worst_input * TimeWarp.fixedDeltaTime * Malfunction.Penalty(part));
                }
              }

              // undo stock behaviour by forcing last_update_time to now
              module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString());
            }
          }
          // drill
          // note: ignore stock temperature mechanic of harvesters
          // note: ignore autoshutdown
          // note: ignore depletion (stock seem to do the same)
          // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)
          else if (module.moduleName == "ModuleResourceHarvester")
          {
            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              // get module from prefab
              ModuleResourceHarvester harvester = part_prefab.Modules.GetModules<ModuleResourceHarvester>()[0];

              // [disabled] reason: not working
              // deduce crew bonus
              /*double experience_bonus = 0.0;
              if (harvester.UseSpecialistBonus)
              {
                foreach(ProtoCrewMember c in vessel.protoVessel.GetVesselCrew())
                {
                  experience_bonus = Math.Max(experience_bonus, (c.trait == harvester.Specialty) ? (double)c.experienceLevel : 0.0);
                }
              }*/
              const double crew_bonus = 1.0; //harvester.SpecialistBonusBase + (experience_bonus + 1.0) * harvester.SpecialistEfficiencyFactor;

              // detect amount of ore in the ground
              AbundanceRequest request = new AbundanceRequest
              {
                Altitude = vessel.altitude,
                BodyId = vessel.mainBody.flightGlobalsIndex,
                CheckForLock = false,
                Latitude = vessel.latitude,
                Longitude = vessel.longitude,
                ResourceType = (HarvestTypes)harvester.HarvesterType,
                ResourceName = harvester.ResourceName
              };
              double abundance = ResourceMap.Instance.GetAbundance(request);

              // if there is actually something (should be if active when unloaded)
              if (abundance > harvester.HarvestThreshold)
              {
                // calculate worst required resource percentual
                double worst_input = 1.0;
                foreach(var ir in harvester.inputList)
                {
                  double required = ir.Ratio * TimeWarp.fixedDeltaTime;
                  double amount = Lib.GetResourceAmount(vessel, ir.ResourceName);
                  worst_input = Math.Min(worst_input, amount / required);
                }

                // for each input resource
                foreach(var ir in harvester.inputList)
                {
                  // consume the resource
                  Lib.RequestResource(vessel, ir.ResourceName, ir.Ratio * worst_input * TimeWarp.fixedDeltaTime);
                }

                // determine resource produced
                double res = abundance * harvester.Efficiency * crew_bonus * worst_input * Malfunction.Penalty(part);

                // accumulate ore
                Lib.RequestResource(vessel, harvester.ResourceName, -res * TimeWarp.fixedDeltaTime);
              }

              // undo stock behaviour by forcing last_update_time to now
              module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString());
            }
          }
          // asteroid drill
          // note: untested
          // note: ignore stock temperature mechanic of asteroid drills
          // note: ignore autoshutdown
          // note: 'undo' stock behaviour by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)
          else if (module.moduleName == "ModuleAsteroidDrill")
          {
            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              // get module from prefab
              ModuleAsteroidDrill asteroid_drill = part_prefab.Modules.GetModules<ModuleAsteroidDrill>()[0];

              // [disabled] reason: not working
              // deduce crew bonus
              /*double experience_bonus = 0.0;
              if (asteroid_drill.UseSpecialistBonus)
              {
                foreach(ProtoCrewMember c in vessel.protoVessel.GetVesselCrew())
                {
                  experience_bonus = Math.Max(experience_bonus, (c.trait == asteroid_drill.Specialty) ? (double)c.experienceLevel : 0.0);
                }
              }*/
              const double crew_bonus = 1.0; //asteroid_drill.SpecialistBonusBase + (experience_bonus + 1.0) * asteroid_drill.SpecialistEfficiencyFactor;

              // get asteroid data
              ProtoPartModuleSnapshot asteroid_info = null;
              ProtoPartModuleSnapshot asteroid_resource = null;
              foreach(ProtoPartSnapshot p in vessel.protoVessel.protoPartSnapshots)
              {
                if (asteroid_info == null) asteroid_info = p.modules.Find(k => k.moduleName == "ModuleAsteroidInfo");
                if (asteroid_resource == null) asteroid_resource = p.modules.Find(k => k.moduleName == "ModuleAsteroidResource");
              }

              // if there is actually an asteroid attached to this active asteroid drill (it should)
              if (asteroid_info != null && asteroid_resource != null)
              {
                // get some data
                double mass_threshold = Convert.ToDouble(asteroid_info.moduleValues.GetValue("massThresholdVal"));
                double mass = Convert.ToDouble(asteroid_info.moduleValues.GetValue("currentMassVal"));
                double abundance = Convert.ToDouble(asteroid_resource.moduleValues.GetValue("abundance"));
                string res_name = asteroid_resource.moduleValues.GetValue("resourceName");
                double res_density = PartResourceLibrary.Instance.GetDefinition(res_name).density;

                // if asteroid isn't depleted
                if (mass > mass_threshold && abundance > double.Epsilon)
                {
                  // consume EC
                  double ec_required = asteroid_drill.PowerConsumption * TimeWarp.fixedDeltaTime;
                  double ec_consumed = Lib.RequestResource(vessel, "ElectricCharge", ec_required);
                  double ec_ratio = ec_consumed / ec_required;

                  // determine resource extracted
                  double res_amount = abundance * asteroid_drill.Efficiency * crew_bonus * ec_ratio * TimeWarp.fixedDeltaTime;

                  // produce mined resource
                  Lib.RequestResource(vessel, res_name, -res_amount);

                  // consume asteroid mass
                  asteroid_info.moduleValues.SetValue("currentMassVal", (mass - res_density * res_amount).ToString());
                }
              }

              // undo stock behaviour by forcing last_update_time to now
              module.moduleValues.SetValue("lastUpdateTime", Planetarium.GetUniversalTime().ToString());
            }
          }
          // science lab
          // note: we are only simulating the EC consumption
          // note: there is no easy way to 'stop' the lab when there isn't enough EC
          else if (module.moduleName == "ModuleScienceConverter")
          {
            // get module from prefab
            ModuleScienceConverter lab = part_prefab.Modules.GetModules<ModuleScienceConverter>()[0];

            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              Lib.RequestResource(vessel, "ElectricCharge", lab.powerRequirement * TimeWarp.fixedDeltaTime);
            }
          }
          // SCANSAT support
          else if (module.moduleName == "SCANsat" || module.moduleName == "ModuleSCANresourceScanner")
          {
            // get ec consumption rate
            PartModule scansat = part_prefab.Modules[module.moduleName];
            double power = Lib.ReflectionValue<float>(scansat, "power");
            double ec_required = power * TimeWarp.fixedDeltaTime;
            bool is_scanning = Lib.GetProtoValue<bool>(module, "scanning");
            bool was_disabled = vd.scansat_id.Contains(part.flightID);

            // if its scanning
            if (Lib.GetProtoValue<bool>(module, "scanning"))
            {
              // consume ec
              double ec_consumed = Lib.RequestResource(vessel, "ElectricCharge", ec_required);

              // if there isn't enough ec
              if (ec_consumed < ec_required * 0.99 && ec_required > double.Epsilon)
              {
                // unregister scanner
                SCANsat.stopScanner(vessel, module, part_prefab);

                // remember disabled scanner
                vd.scansat_id.Add(part.flightID);

                // give the user some feedback
                if (DB.VesselData(vessel.id).cfg_ec == 1)
                  Message.Post("SCANsat sensor was disabled on <b>" + vessel.vesselName + "</b>");
              }
            }
            // if it was disabled
            else if (vd.scansat_id.Contains(part.flightID))
            {
              // if there is enough ec
              double ec_amount = Lib.GetResourceAmount(vessel, "ElectricCharge");
              double ec_capacity = Lib.GetResourceCapacity(vessel, "ElectricCharge");
              if (ec_capacity > double.Epsilon && ec_amount / ec_capacity > 0.25) //< re-enable at 25% EC
              {
                // re-enable the scanner
                SCANsat.resumeScanner(vessel, module, part_prefab);

                // give the user some feedback
                if (DB.VesselData(vessel.id).cfg_ec == 1)
                  Message.Post("SCANsat sensor resumed operations on <b>" + vessel.vesselName + "</b>");
              }
            }

            // forget active scanners
            if (Lib.GetProtoValue<bool>(module, "scanning")) vd.scansat_id.Remove(part.flightID);
          }
          // NearFutureSolar support
          // note: we assume deployed, this is a current limitation
          else if (module.moduleName == "ModuleCurvedSolarPanel")
          {
            // if in sunlight
            if (info.sunlight)
            {
              PartModule curved_panel = part_prefab.Modules[module.moduleName];
              double output = CurvedPanelOutput(vessel, part, part_prefab, curved_panel, info.sun_dir, info.sun_dist, atmo_factor) * Malfunction.Penalty(part);
              Lib.RequestResource(vessel, "ElectricCharge", -output * TimeWarp.fixedDeltaTime);
            }
          }
          // NearFutureElectrical support
          // note: fission generator ignore heat
          // note: radioisotope generator doesn't support easy mode
          else if (module.moduleName == "FissionGenerator")
          {
            PartModule generator = part_prefab.Modules[module.moduleName];
            double power = Lib.ReflectionValue<float>(generator, "PowerGeneration");

            // get fission reactor tweakable, will default to 1.0 for other modules
            var reactor = part.modules.Find(k => k.moduleName == "FissionReactor");
            double tweakable = reactor == null ? 1.0 : Lib.ConfigValue(reactor.moduleValues, "CurrentPowerPercent", 100.0) * 0.01;
            Lib.RequestResource(vessel, "ElectricCharge", -power * tweakable * TimeWarp.fixedDeltaTime);
          }
          else if (module.moduleName == "ModuleRadioisotopeGenerator")
          {
            double mission_time = vessel.missionTime / (3600.0 * Lib.HoursInDay() * Lib.DaysInYear());
            PartModule generator = part_prefab.Modules[module.moduleName];
            double half_life = Lib.ReflectionValue<float>(generator, "HalfLife");
            double remaining = Math.Pow(2.0, (-mission_time) / half_life);
            double power = Lib.ReflectionValue<float>(generator, "BasePower");
            Lib.RequestResource(vessel, "ElectricCharge", -power * remaining * TimeWarp.fixedDeltaTime);
          }
          // KERBALISM modules
          else if (module.moduleName == "Scrubber") { Scrubber.BackgroundUpdate(vessel, part.flightID); }
          else if (module.moduleName == "Greenhouse") { Greenhouse.BackgroundUpdate(vessel, part.flightID); }
          else if (module.moduleName == "GravityRing") { GravityRing.BackgroundUpdate(vessel, part.flightID); }
          else if (module.moduleName == "Malfunction") { Malfunction.BackgroundUpdate(vessel, part.flightID); }
        }
      }
    }
  }
示例#23
0
  // implement greenhouse mechanics for unloaded vessels
  public static void BackgroundUpdate(Vessel vessel, uint flight_id)
  {
    // get data
    ProtoPartModuleSnapshot m = Lib.GetProtoModule(vessel, flight_id, "Greenhouse");
    double ec_rate = Lib.GetProtoValue<double>(m, "ec_rate");
    double waste_rate = Lib.GetProtoValue<double>(m, "waste_rate");
    double harvest_size = Lib.GetProtoValue<double>(m, "harvest_size");
    double growth_rate = Lib.GetProtoValue<double>(m, "growth_rate");
    bool door_opened = Lib.GetProtoValue<bool>(m, "door_opened");
    double growth = Lib.GetProtoValue<double>(m, "growth");
    float lamps = Lib.GetProtoValue<float>(m, "lamps");
    double lighting = Lib.GetProtoValue<double>(m, "lighting");

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

    // consume ec for lighting
    double ec_light_perc = 0.0;
    if (lamps > float.Epsilon)
    {
      double ec_light_required = ec_rate * elapsed_s * lamps;
      double ec_light = Lib.RequestResource(vessel, "ElectricCharge", ec_light_required);
      ec_light_perc = ec_light / ec_light_required;

      // if there isn't enough ec for lighting
      if (ec_light <= double.Epsilon)
      {
        // shut down the light
        lamps = 0.0f;
      }
    }


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


    // determine lighting conditions
    // note: we ignore sun direction for gameplay reasons: else the user must reorient the greenhouse as the planets dance over time
    // - natural light depend on: distance from sun, direct sunlight, door status
    // - artificial light depend on: lamps tweakable and ec available, door status
    lighting = NaturalLighting(info.sun_dist) * (info.sunlight ? 1.0 : 0.0) * (door_opened ? 1.0 : 0.0)
             + lamps * ec_light_perc * (door_opened ? 1.0 : 1.0 + Settings.GreenhouseDoorBonus);


    // consume waste
    double waste_required = waste_rate * elapsed_s;
    double waste = Lib.RequestResource(vessel, "Waste", waste_required);
    double waste_perc = waste / waste_required;


    // determine growth bonus
    double growth_bonus = 0.0;
    growth_bonus += Settings.GreenhouseSoilBonus * (Lib.Landed(vessel) ? 1.0 : 0.0);
    growth_bonus += Settings.GreenhouseWasteBonus * waste_perc;

    // grow the crop
    growth += elapsed_s * (growth_rate * (1.0 + growth_bonus)) * lighting;

    // if it is harvest time
    if (growth >= 1.0)
    {
      // reset growth
      growth = 0.0;

      // produce food
      Lib.RequestResource(vessel, "Food", -harvest_size);

      // show a message to the user
      Message.Post("On <color=FFFFFF>" + vessel.vesselName + "</color> the crop harvest produced <color=FFFFFF>" + harvest_size.ToString("F0") + " Food</color>");
    }

    // store data
    Lib.SetProtoValue(m, "growth", growth);
    Lib.SetProtoValue(m, "lighting", lighting);
    Lib.SetProtoValue(m, "lamps", lamps);
  }