예제 #1
0
 // calculate a cache entry for the vessel
 static vessel_info Compute(Vessel v)
 {
   vessel_info info = new vessel_info();
   info.position = Lib.VesselPosition(v);
   info.sunlight = Sim.RaytraceBody(v, Sim.Sun(), out info.sun_dir, out info.sun_dist);
   info.temperature = Sim.Temperature(v, info.sunlight);
   info.cosmic_radiation = Radiation.CosmicRadiation(v);
   info.belt_radiation = Radiation.BeltRadiation(v);
   info.storm_radiation = Radiation.StormRadiation(v, info.sunlight);
   info.env_radiation = info.cosmic_radiation + info.belt_radiation + info.storm_radiation;
   info.breathable = Sim.Breathable(v);
   foreach(var p in Kerbalism.rules)
   {
     Rule r = p.Value;
     if (r.resource_name.Length > 0)
     {
       var vmon = new vmon_cache();
       vmon.depletion = r.EstimateLifetime(v);
       double amount = Lib.GetResourceAmount(v, r.resource_name);
       double capacity = Lib.GetResourceCapacity(v, r.resource_name);
       vmon.level = capacity > double.Epsilon ? amount / capacity : 1.0; //< level is 1 with no capacity
       info.vmon.Add(p.Value.name, vmon);
     }
   }
   return info;
 }
예제 #2
0
  [KSPField(isPersistant = true)] public bool is_dead = false;            // indicate if eva kerbal is dead


  public void FixedUpdate()
  {
    // get KerbalEVA module
    KerbalEVA kerbal = part.FindModuleImplementing<KerbalEVA>();

    // show/hide helmet
    SetHelmet(kerbal, has_helmet);

    // consume EC for the headlamp
    if (has_helmet && kerbal.lampOn) part.RequestResource("ElectricCharge", Settings.HeadlightCost * TimeWarp.fixedDeltaTime);

    // determine if it has EC left
    bool ec_left = Lib.GetResourceAmount(part, "ElectricCharge") > double.Epsilon;

    // force the headlamp lights on/off depending on ec amount left and if it has an helmet
    SetHeadlamp(kerbal, has_helmet && kerbal.lampOn && ec_left);

    // synchronize helmet flares with headlamp state
    SetFlares(kerbal, has_helmet && kerbal.lampOn && ec_left);

    // if dead
    if (is_dead)
    {
      // enforce freezed state
      SetFreezed(kerbal);

      // remove plant flag action
      kerbal.flagItems = 0;

      // remove experiment actions (game engine keeps readding them)
      RemoveExperiments(kerbal);
    }
  }
예제 #3
0
  public static radiation_data analyze_radiation(List<Part> parts, environment_data env, crew_data crew)
  {
    // store data
    radiation_data radiation = new radiation_data();

    // scan the parts
    foreach(Part p in parts)
    {
      // accumulate shielding amount and capacity
      radiation.shielding_amount += Lib.GetResourceAmount(p, "Shielding");
      radiation.shielding_capacity += Lib.GetResourceCapacity(p, "Shielding");
    }

    // calculate radiation data
    double shielding = Radiation.Shielding(radiation.shielding_amount, radiation.shielding_capacity);
    double belt_strength = Settings.BeltRadiation * Radiation.Dynamo(env.body) * 0.5; //< account for the 'ramp'
    if (crew.capacity > 0)
    {
      radiation.life_expectancy = new double[]
      {
        Settings.RadiationFatalThreshold / (Settings.CosmicRadiation * (1.0 - shielding)),
        Settings.RadiationFatalThreshold / (Settings.StormRadiation * (1.0 - shielding)),
        Radiation.HasBelt(env.body) ? Settings.RadiationFatalThreshold / (belt_strength * (1.0 - shielding)) : double.NaN
      };
    }
    else
    {
      radiation.life_expectancy = new double[]{double.NaN, double.NaN, double.NaN};
    }

    // return data
    return radiation;
  }
예제 #4
0
  GUIContent indicator_ec(Vessel v)
  {
    double amount = Lib.GetResourceAmount(v, "ElectricCharge");
    double capacity = Lib.GetResourceCapacity(v, "ElectricCharge");
    double level = capacity > 0.0 ? amount / capacity : 1.0;

    GUIContent state = new GUIContent();
    state.tooltip = capacity > 0.0 ? "EC: " + (level * 100.0).ToString("F0") + "%" : "";
    state.image = icon_battery_nominal;

    double low_threshold = 0.15;
    foreach(var p in Kerbalism.rules)
    {
      Rule r = p.Value;
      if (r.resource_name == "ElectricCharge")
      {
        low_threshold = r.low_threshold;
        break;
      }
    }

    if (level <= double.Epsilon) state.image = icon_battery_danger;
    else if (level <= low_threshold) state.image = icon_battery_warning;
    return state;
  }
예제 #5
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));
    }
  }
예제 #6
0
        public static double Shielding(ConnectedLivingSpace.ICLSSpace space)
        {
            double amount   = 0.0;
            double capacity = 0.0;

            foreach (var part in space.Parts)
            {
                amount   += Lib.GetResourceAmount(part.Part, "Shielding");
                capacity += Lib.GetResourceCapacity(part.Part, "Shielding");
            }
            return(Shielding(amount, capacity));
        }
예제 #7
0
  void problem_recyclers(Vessel v, List<Scrubber> scrubbers, List<Recycler> recyclers, ref List<Texture> icons, ref List<string> tooltips)
  {
    bool no_ec_left = Lib.GetResourceAmount(v, "ElectricCharge") <= double.Epsilon;

    HashSet<string> disabled_recyclers = new HashSet<string>();
    HashSet<string> no_power_recyclers = new HashSet<string>();
    HashSet<string> no_filter_recyclers = new HashSet<string>();

    // analyze scrubbers
    foreach(Scrubber scrubber in scrubbers)
    {
      if (!scrubber.is_enabled) disabled_recyclers.Add("Scrubber");
      else if (no_ec_left) no_power_recyclers.Add("Scrubber");
    }

    // analyze recyclers
    foreach(Recycler recycler in recyclers)
    {
      if (!recycler.is_enabled) disabled_recyclers.Add(recycler.display_name);
      else if (no_ec_left) no_power_recyclers.Add(recycler.display_name);
      else
      {
        if (recycler.filter_name.Length > 0 && recycler.filter_rate > 0.0)
        {
          if (Lib.GetResourceAmount(v, recycler.filter_name) <= double.Epsilon)
          {
            no_filter_recyclers.Add(recycler.display_name);
          }
        }
      }
    }

    if (disabled_recyclers.Count > 0)
    {
      icons.Add(icon_scrubber_warning);
      foreach(string s in disabled_recyclers) tooltips.Add(s + " disabled");
    }

    if (no_power_recyclers.Count > 0)
    {
      icons.Add(icon_scrubber_danger);
      foreach(string s in no_power_recyclers) tooltips.Add(s + " has no power");
    }

    if (no_filter_recyclers.Count > 0)
    {
      icons.Add(icon_scrubber_danger);
      foreach(string s in no_filter_recyclers) tooltips.Add(s + " has no filter left");
    }
  }
예제 #8
0
  public static oxygen_data analyze_oxygen(List<Part> parts, environment_data env, crew_data crew)
  {
    // store data
    oxygen_data oxygen = new oxygen_data();

    // get scrubber efficiency
    oxygen.scrubber_efficiency = Scrubber.DeduceEfficiency();

    // calculate oxygen consumed
    oxygen.consumed = !env.breathable ? (double)crew.count * Settings.OxygenPerSecond : 0.0;

    // deduce co2 produced by the crew per-second
    double simulated_co2 = oxygen.consumed;

    // scan the parts
    foreach(Part p in parts)
    {
      // accumulate food storage
      oxygen.storage += Lib.GetResourceAmount(p, "Oxygen");

      // for each module
      foreach(PartModule m in p.Modules)
      {
        // scrubber
        if (m.moduleName == "Scrubber")
        {
          Scrubber mm = (Scrubber)m;

          // do nothing inside breathable atmosphere
          if (mm.is_enabled && !env.breathable)
          {
            double co2_scrubbed = Math.Min(simulated_co2, mm.co2_rate);
            if (co2_scrubbed > double.Epsilon)
            {
              oxygen.scrubber_cost += mm.ec_rate * (co2_scrubbed / mm.co2_rate);
              oxygen.recycled += co2_scrubbed * oxygen.scrubber_efficiency;
              simulated_co2 -= co2_scrubbed;
            }
          }
        }
      }
    }

    // calculate life expectancy
    oxygen.life_expectancy = !env.breathable ? oxygen.storage / Math.Max(oxygen.consumed - oxygen.recycled, 0.0) : double.NaN;

    // return data
    return oxygen;
  }
예제 #9
0
  GUIContent indicator_ec(Vessel v)
  {
    // note: if there isn't ec capacity, show danger

    double amount = Lib.GetResourceAmount(v, "ElectricCharge");
    double capacity = Lib.GetResourceCapacity(v, "ElectricCharge");
    double level = capacity > 0.0 ? amount / capacity : 0.0;

    GUIContent state = new GUIContent();
    state.tooltip = "EC: " + (level * 100.0).ToString("F0") + "%";
    if (level <= Settings.ResourceDangerThreshold) state.image = icon_battery_danger;
    else if (level <= Settings.ResourceWarningThreshold) state.image = icon_battery_warning;
    else state.image = icon_battery_nominal;
    return state;
  }
예제 #10
0
  // estimate lifetime for the rule resource
  // note: this function must be called only once for simulation step
  // return 0 if no resource left, NaN if rate of change is positive, or seconds left in other cases
  public double EstimateLifetime(Vessel v)
  {
    // get prev amount for the vessel
    double prev_amount = 0.0;
    if (!prev_amounts.TryGetValue(v.id, out prev_amount)) prev_amount = 0.0;

    // calculate delta
    double amount = Lib.GetResourceAmount(v, this.resource_name);
    double meal_rate = this.interval > double.Epsilon ? this.rate / this.interval : 0.0;
    double delta = (amount - prev_amount) / TimeWarp.fixedDeltaTime - meal_rate * Lib.CrewCount(v);

    // remember prev amount
    prev_amounts[v.id] = amount;

    // return lifetime in seconds
    return amount <= double.Epsilon ? 0.0 : delta >= -double.Epsilon ? double.NaN : amount / -delta;
  }
예제 #11
0
 void problem_scrubbers(Vessel v, List<Scrubber> scrubbers, ref List<Texture> icons, ref List<string> tooltips)
 {
   bool no_ec_left = Lib.GetResourceAmount(v, "ElectricCharge") <= double.Epsilon;
   bool scrubber_disabled = false;
   bool scrubber_nopower = false;
   foreach(Scrubber scrubber in scrubbers)
   {
     scrubber_disabled |= !scrubber.is_enabled;
     scrubber_nopower |= scrubber.is_enabled && no_ec_left;
   }
   if (scrubber_disabled)
   {
     icons.Add(icon_scrubber_warning);
     tooltips.Add("Scrubber disabled");
   }
   if (scrubber_nopower)
   {
     icons.Add(icon_scrubber_danger);
     tooltips.Add("Scrubber has no power");
   }
 }
예제 #12
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);
    }
  }
예제 #13
0
  void IScienceDataTransmitter.TransmitData(List<ScienceData> dataQueue)
  {
    // if there is no signal
    if (!can_transmit)
    {
      // show a message to the user
      Message.Post(Severity.warning, "No signal", "We can't send the data");

      // return data to the containers
      ReturnData(dataQueue);

      // do not transmit the data
      return;
    }

    // calculate total ec cost of transmission
    double total_amount = 0.0;
    foreach(ScienceData sd in dataQueue) total_amount += sd.dataAmount;
    double total_cost = total_amount * this.packetResourceCost;

    // if there is no EC to transmit the data
    if (total_cost > Lib.GetResourceAmount(vessel, "ElectricCharge"))
    {
      // show a message to the user
      Message.Post(Severity.warning, "Not enough power, <b>" +  total_cost.ToString("F0") + " ElectricCharge</b> required", "We can't send the data");

      // return data to the containers
      ReturnData(dataQueue);

      // do not transmit the data
      return;
    }

    // transmit the data
    ModuleDataTransmitter transmitter = (ModuleDataTransmitter)this;
    transmitter.TransmitData(dataQueue);
  }
예제 #14
0
        void updateThermometers(Vessel v)
        {
            // get temperature
            double temp = Cache.VesselInfo(v).temperature;

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

            // replace thermometer sensor readings with our own, to give the user some feedback about climate control mechanic
            foreach (ModuleEnviroSensor m in v.FindPartModulesImplementing <ModuleEnviroSensor>())
            {
                if (m.sensorType == "TEMP" && m.sensorActive)
                {
                    if (ec_amount <= double.Epsilon)
                    {
                        m.readoutInfo = "NO POWER!";
                    }
                    else
                    {
                        m.readoutInfo = Lib.HumanReadableTemp(temp);
                    }
                }
            }
        }
예제 #15
0
 public void Update()
 {
     Animation[] anim = this.part.FindModelAnimators(animation_name);
     if (anim.Length > 0)
     {
         double ls           = Lib.GetResourceAmount(this.part, resource_name);
         double capacity     = Lib.GetResourceCapacity(this.part, resource_name);
         double red_capacity = capacity * threshold;
         if (ls <= red_capacity && green_status)
         {
             anim[0][animation_name].normalizedTime = 0.0f;
             anim[0][animation_name].speed          = Math.Abs(anim[0][animation_name].speed);
             anim[0].Play(animation_name);
             green_status = false;
         }
         if (ls > red_capacity && !green_status)
         {
             anim[0][animation_name].normalizedTime = 1.0f;
             anim[0][animation_name].speed          = -Math.Abs(anim[0][animation_name].speed);
             anim[0].Play(animation_name);
             green_status = true;
         }
     }
 }
예제 #16
0
  void Breakdown(Vessel v, ProtoCrewMember c)
  {
    // constants
    const double food_penality = 0.2;        // proportion of food lost on 'depressed'
    const double oxygen_penality = 0.2;      // proportion of oxygen lost on 'wrong_valve'

    // get info
    double food_amount = Lib.GetResourceAmount(v, "Food");
    double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen");

    // compile list of events with condition satisfied
    List<KerbalBreakdown> events = new List<KerbalBreakdown>();
    events.Add(KerbalBreakdown.mumbling); //< do nothing, here so there is always something that can happen
    if (Lib.CrewCount(v) > 1) events.Add(KerbalBreakdown.argument); //< do nothing, add some variation to messages
    if (Lib.HasData(v)) events.Add(KerbalBreakdown.fat_finger);
    if (Malfunction.CanMalfunction(v)) events.Add(KerbalBreakdown.rage);
    if (food_amount > double.Epsilon) events.Add(KerbalBreakdown.depressed);
    if (oxygen_amount > double.Epsilon) events.Add(KerbalBreakdown.wrong_valve);

    // choose a breakdown event
    KerbalBreakdown breakdown = events[Lib.RandomInt(events.Count)];

    // post message first so this one is shown before malfunction message
    Message.Post(Severity.breakdown, KerbalEvent.stress, v, c, breakdown);

    // trigger the event
    switch(breakdown)
    {
      case KerbalBreakdown.mumbling: break; // do nothing
      case KerbalBreakdown.argument: break; // do nothing
      case KerbalBreakdown.fat_finger: Lib.RemoveData(v); break;
      case KerbalBreakdown.rage: Malfunction.CauseMalfunction(v); break;
      case KerbalBreakdown.depressed: Lib.RequestResource(v, "Food", food_amount * food_penality); break;
      case KerbalBreakdown.wrong_valve: Lib.RequestResource(v, "Oxygen", oxygen_amount * oxygen_penality); break;
    }
  }
예제 #17
0
  // implement recycler mechanics
  public void FixedUpdate()
  {
    // do nothing in the editor
    if (HighLogic.LoadedSceneIsEditor) return;

    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
      this.part.RequestResource(waste_name, waste_required * worst_input);
      this.part.RequestResource("ElectricCharge", ec_required * worst_input);
      this.part.RequestResource(filter_name, filter_required * worst_input);
      this.part.RequestResource(resource_name, -waste_required * worst_input * waste_ratio);

      // set status
      Status = waste_amount <= double.Epsilon ? "No " + waste_name : ec_amount <= double.Epsilon ? "No Power" : "Running";
    }
    else
    {
      Status = "Off";
    }
  }
예제 #18
0
  public static ec_data analyze_ec(List<Part> parts, environment_data env, crew_data crew, food_data food, oxygen_data oxygen, signal_data signal)
  {
    // store data
    ec_data ec = new ec_data();

    // calculate climate cost
    ec.consumed = (double)crew.count * env.temp_diff * Settings.ElectricChargePerSecond;

    // scan the parts
    foreach(Part p in parts)
    {
      // accumulate EC storage
      ec.storage += Lib.GetResourceAmount(p, "ElectricCharge");

      // remember if we already considered a resource converter module
      // rationale: we assume only the first module in a converter is active
      bool first_converter = true;

      // for each module
      foreach(PartModule m in p.Modules)
      {
        // command
        if (m.moduleName == "ModuleCommand")
        {
          ModuleCommand mm = (ModuleCommand)m;
          foreach(ModuleResource res in mm.inputResources)
          {
            if (res.name == "ElectricCharge")
            {
              ec.consumed += res.rate;
            }
          }
        }
        // solar panel
        else if (m.moduleName == "ModuleDeployableSolarPanel")
        {
          ModuleDeployableSolarPanel mm = (ModuleDeployableSolarPanel)m;
          double solar_k = (mm.useCurve ? mm.powerCurve.Evaluate((float)env.sun_dist) : env.sun_flux / Sim.SolarFluxAtHome());
          double generated = mm.chargeRate * solar_k * env.atmo_factor;
          ec.generated_sunlight += generated;
          ec.best_ec_generator = Math.Max(ec.best_ec_generator, generated);
        }
        // generator
        else if (m.moduleName == "ModuleGenerator")
        {
          // skip launch clamps, that include a generator
          if (p.partInfo.name == "launchClamp1") continue;

          ModuleGenerator mm = (ModuleGenerator)m;
          foreach(ModuleResource res in mm.inputList)
          {
            if (res.name == "ElectricCharge")
            {
              ec.consumed += res.rate;
            }
          }
          foreach(ModuleResource res in mm.outputList)
          {
            if (res.name == "ElectricCharge")
            {
              ec.generated_shadow += res.rate;
              ec.generated_sunlight += res.rate;
              ec.best_ec_generator = Math.Max(ec.best_ec_generator, res.rate);
            }
          }
        }
        // converter
        // note: only electric charge is considered for resource converters
        // note: we only consider the first resource converter in a part, and ignore the rest
        else if (m.moduleName == "ModuleResourceConverter" && first_converter)
        {
          ModuleResourceConverter mm = (ModuleResourceConverter)m;
          foreach(ResourceRatio rr in mm.inputList)
          {
            if (rr.ResourceName == "ElectricCharge")
            {
              ec.consumed += rr.Ratio;
            }
          }
          foreach(ResourceRatio rr in mm.outputList)
          {
            if (rr.ResourceName == "ElectricCharge")
            {
              ec.generated_shadow += rr.Ratio;
              ec.generated_sunlight += rr.Ratio;
              ec.best_ec_generator = Math.Max(ec.best_ec_generator, rr.Ratio);
            }
          }
          first_converter = false;
        }
        // harvester
        // note: only electric charge is considered for resource harvesters
        else if (m.moduleName == "ModuleResourceHarvester")
        {
          ModuleResourceHarvester mm = (ModuleResourceHarvester)m;
          foreach(ResourceRatio rr in mm.inputList)
          {
            if (rr.ResourceName == "ElectricCharge")
            {
              ec.consumed += rr.Ratio;
            }
          }
        }
        // active radiators
        else if (m.moduleName == "ModuleActiveRadiator")
        {
          ModuleActiveRadiator mm = (ModuleActiveRadiator)m;
          if (mm.IsCooling)
          {
            foreach(var rr in mm.inputResources)
            {
              if (rr.name == "ElectricCharge")
              {
                ec.consumed += rr.rate;
              }
            }
          }
        }
        // wheels
        else if (m.moduleName == "ModuleWheelMotor")
        {
          ModuleWheelMotor mm = (ModuleWheelMotor)m;
          if (mm.motorEnabled && mm.inputResource.name == "ElectricCharge")
          {
            ec.consumed += mm.inputResource.rate;
          }
        }
        else if (m.moduleName == "ModuleWheelMotorSteering")
        {
          ModuleWheelMotorSteering mm = (ModuleWheelMotorSteering)m;
          if (mm.motorEnabled && mm.inputResource.name == "ElectricCharge")
          {
            ec.consumed += mm.inputResource.rate;
          }
        }
        // SCANsat support
        else if (m.moduleName == "SCANsat" || m.moduleName == "ModuleSCANresourceScanner")
        {
          // include it in ec consumption, if deployed
          if (SCANsat.isDeployed(p, m)) ec.consumed += Lib.ReflectionValue<float>(m, "power");
        }
        // NearFutureSolar support
        // note: assume half the components are in sunlight, and average inclination is half
        else if (m.moduleName == "ModuleCurvedSolarPanel")
        {
          // get total rate
          double tot_rate = Lib.ReflectionValue<float>(m, "TotalEnergyRate");

          // get number of components
          int components = p.FindModelTransforms(Lib.ReflectionValue<string>(m, "PanelTransformName")).Length;

          // approximate output
          // 0.7071: average clamped cosine
          ec.generated_sunlight += 0.7071 * tot_rate;
        }
      }
    }

    // include cost from greenhouses artificial lighting
    ec.consumed += food.greenhouse_cost;

    // include cost from scrubbers
    ec.consumed += oxygen.scrubber_cost;

    // include relay cost for the best relay antenna
    ec.consumed += signal.relay_cost;

    // finally, calculate life expectancy of ec
    ec.life_expectancy_sunlight = ec.storage / Math.Max(ec.consumed - ec.generated_sunlight, 0.0);
    ec.life_expectancy_shadow = ec.storage / Math.Max(ec.consumed - ec.generated_shadow, 0.0);

    // return data
    return ec;
  }
예제 #19
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 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)
        {
          // 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)
          else if (module.moduleName == "ModuleResourceConverter")
          {
            // determine if active
            bool activated = Convert.ToBoolean(module.moduleValues.GetValue("IsActivated"));

            // if active
            if (activated)
            {
              // get module from prefab
              ModuleResourceConverter converter = part_prefab.Modules.GetModules<ModuleResourceConverter>()[converter_index++];

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

              // 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);
                }
              }
              double crew_bonus = 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];

              // 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);
                }
              }
              double crew_bonus = 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());
            }
          }
          // SCANSAT support (new version)
          // TODO: enable better 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;

            // if it was scanning
            if (SCANsat.wasScanning(module))
            {
              // 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.15) //< re-enable at 15% 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(Severity.relax, "SCANsat> sensor on <b>" + vessel.vesselName + "</b> resumed operations", "we got enough ElectricCharge");
              }
            }

            // if it is scanning
            if (SCANsat.isScanning(module))
            {
              // 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, and remember it
                SCANsat.stopScanner(vessel, module, part_prefab);

                // give the user some feedback
                if (DB.VesselData(vessel.id).cfg_ec == 1)
                  Message.Post(Severity.warning, "SCANsat sensor was disabled on <b>" + vessel.vesselName + "</b>", "for lack of ElectricCharge");
              }
            }
          }*/
          // SCANSAT support (old version)
          // note: this one doesn't support re-activation, is a bit slower and less clean
          //       waiting for DMagic to fix a little bug
          else if (module.moduleName == "SCANsat" || module.moduleName == "ModuleSCANresourceScanner")
          {
            // determine if scanning
            bool scanning = Convert.ToBoolean(module.moduleValues.GetValue("scanning"));

            // consume ec
            if (scanning)
            {
              // get ec consumption
              PartModule scansat = part_prefab.Modules[module.moduleName];
              double power = Lib.ReflectionValue<float>(scansat, "power");

              // consume ec
              double ec_required = power * TimeWarp.fixedDeltaTime;
              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 using reflection
                foreach(var a in AssemblyLoader.loadedAssemblies)
                {
                  if (a.name == "SCANsat")
                  {
                    Type controller_type = a.assembly.GetType("SCANsat.SCANcontroller");
                    System.Object controller = controller_type.GetProperty("controller", BindingFlags.Public | BindingFlags.Static).GetValue(null, null);
                    controller_type.InvokeMember("removeVessel", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, controller, new System.Object[]{vessel});
                  }
                }

                // disable scanning
                module.moduleValues.SetValue("scanning", false.ToString());

                // give the user some feedback
                if (DB.VesselData(vessel.id).cfg_ec == 1)
                  Message.Post(Severity.warning, "SCANsat sensor was disabled on <b>" + vessel.vesselName + "</b>", "for lack of ElectricCharge");
              }
            }
          }
          // NearFutureSolar support
          // note: we assume deployed, this is a current limitation
          else if (module.moduleName == "ModuleCurvedSolarPanel")
          {
            // [unused] determine if extended
            //string state = module.moduleValues.GetValue("SavedState");
            //bool extended = state == ModuleDeployableSolarPanel.panelStates.EXTENDED.ToString();

            // if in sunlight
            if (info.sunlight)
            {
              // produce electric charge
              double output = CurvedPanelOutput(vessel, part, part_prefab, info.sun_dir, info.sun_dist, atmo_factor) * Malfunction.Penalty(part);
              Lib.RequestResource(vessel, "ElectricCharge", -output * 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 == "Malfunction") { Malfunction.BackgroundUpdate(vessel, part.flightID); }
        }
      }
    }
  }
예제 #20
0
  GUIContent indicator_supplies(Vessel v, List<Scrubber> scrubbers, List<Greenhouse> greenhouses)
  {
    // get food & oxygen info
    double food_amount = Lib.GetResourceAmount(v, "Food");
    double food_capacity = Lib.GetResourceCapacity(v, "Food");
    double food_level = food_capacity > 0.0 ? food_amount / food_capacity : 1.0;
    double oxygen_amount = Lib.GetResourceAmount(v, "Oxygen");
    double oxygen_capacity = Lib.GetResourceCapacity(v, "Oxygen");
    double oxygen_level = oxygen_capacity > 0.0 ? oxygen_amount / oxygen_capacity : 1.0;
    double level = Math.Min(food_level, oxygen_level);

    // store the icon and tooltip
    GUIContent state = new GUIContent();

    // choose an icon
    if (level <= Settings.ResourceDangerThreshold) state.image = icon_supplies_danger;
    else if (level <= Settings.ResourceWarningThreshold) state.image = icon_supplies_warning;
    else state.image = icon_supplies_nominal;

    // if there is someone on board
    List<string> tooltips = new List<string>();
    int crew_count = Lib.CrewCount(v);
    if (crew_count > 0)
    {
      // get oxygen recycled by scrubbers
      double oxygen_recycled = 0.0;
      double ec_left = Lib.GetResourceAmount(v, "ElectricCharge");
      double co2_left = Lib.GetResourceAmount(v, "CO2");
      foreach(Scrubber scrubber in scrubbers)
      {
        if (scrubber.is_enabled)
        {
          double co2_consumed = Math.Max(co2_left, scrubber.co2_rate);
          ec_left -= scrubber.ec_rate;
          co2_left -= co2_consumed;
          if (ec_left > -double.Epsilon && co2_left > -double.Epsilon) oxygen_recycled += co2_consumed * scrubber.efficiency;
          else break;
        }
      }

      // calculate time until depletion for food
      double food_consumption = (double)crew_count * Settings.FoodPerMeal / Settings.MealFrequency;
      if (food_capacity > double.Epsilon && food_consumption > double.Epsilon)
      {
        double food_depletion = food_amount / food_consumption;
        tooltips.Add(food_amount / food_capacity > Settings.ResourceDangerThreshold
          ? "Food: <b>" + (food_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(food_depletion) + "</b>"
          : "Food: <b>depleted</b>");
      }

      // calculate time until depletion for oxygen
      double oxygen_consumption = !LifeSupport.BreathableAtmosphere(v) ? (double)crew_count * Settings.OxygenPerSecond - oxygen_recycled : 0.0;
      if (oxygen_capacity > double.Epsilon && oxygen_consumption > double.Epsilon)
      {
        double oxygen_depletion = oxygen_amount / oxygen_consumption;
        tooltips.Add(oxygen_amount / oxygen_capacity > Settings.ResourceDangerThreshold
          ? "Oxygen: <b>" + (oxygen_level * 100.0).ToString("F0") + "%, </b>deplete in <b>" + Lib.HumanReadableDuration(oxygen_depletion) + "</b>"
          : "Oxygen: <b>depleted</b>");
      }
    }
    state.tooltip = string.Join("\n", tooltips.ToArray());
    return state;
  }
예제 #21
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); }
        }
      }
    }
  }
예제 #22
0
        void resourceWarnings()
        {
            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // skip invalid vessels
                if (!Lib.IsVessel(v))
                {
                    continue;
                }

                // skip resque missions
                if (Lib.IsResqueMission(v))
                {
                    continue;
                }

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

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

                // get EC amount and capacity
                double ec_amount   = Lib.GetResourceAmount(v, "ElectricCharge");
                double ec_capacity = Lib.GetResourceCapacity(v, "ElectricCharge");
                double ec_perc     = ec_capacity > 0.0 ? ec_amount / ec_capacity : 0.0;

                // if it has EC capacity
                if (ec_capacity > 0.0)
                {
                    // check EC thresholds and show messages
                    if (ec_perc <= Settings.ResourceDangerThreshold && vd.msg_ec < 2)
                    {
                        if (vd.cfg_ec == 1)
                        {
                            Message.Post(Severity.danger, VesselEvent.ec, v);
                        }
                        vd.msg_ec = 2;
                    }
                    else if (ec_perc <= Settings.ResourceWarningThreshold && vd.msg_ec < 1)
                    {
                        if (vd.cfg_ec == 1)
                        {
                            Message.Post(Severity.warning, VesselEvent.ec, v);
                        }
                        vd.msg_ec = 1;
                    }
                    else if (ec_perc > Settings.ResourceWarningThreshold && vd.msg_ec > 0)
                    {
                        if (vd.cfg_ec == 1)
                        {
                            Message.Post(Severity.relax, VesselEvent.ec, v);
                        }
                        vd.msg_ec = 0;
                    }
                }

                // get food amount and capacity
                double food_amount   = Lib.GetResourceAmount(v, "Food");
                double food_capacity = Lib.GetResourceCapacity(v, "Food");
                double food_perc     = food_capacity > 0.0 ? food_amount / food_capacity : 0.0;

                // if it has food capacity
                if (food_capacity > 0.0)
                {
                    // check food thresholds and show messages
                    // note: no warnings at prelaunch
                    if (food_perc <= Settings.ResourceDangerThreshold && vd.msg_food < 2)
                    {
                        if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH)
                        {
                            Message.Post(Severity.danger, VesselEvent.food, v);
                        }
                        vd.msg_food = 2;
                    }
                    else if (food_perc <= Settings.ResourceWarningThreshold && vd.msg_food < 1)
                    {
                        if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH)
                        {
                            Message.Post(Severity.warning, VesselEvent.food, v);
                        }
                        vd.msg_food = 1;
                    }
                    else if (food_perc > Settings.ResourceWarningThreshold && vd.msg_food > 0)
                    {
                        if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH)
                        {
                            Message.Post(Severity.relax, VesselEvent.food, v);
                        }
                        vd.msg_food = 0;
                    }
                }

                // get oxygen amount and capacity
                double oxygen_amount   = Lib.GetResourceAmount(v, "Oxygen");
                double oxygen_capacity = Lib.GetResourceCapacity(v, "Oxygen");
                double oxygen_perc     = oxygen_capacity > 0.0 ? oxygen_amount / oxygen_capacity : 0.0;

                // if it has oxygen capacity
                if (oxygen_capacity > 0.0)
                {
                    // check oxygen thresholds and show messages
                    // note: no warnings at prelaunch
                    if (oxygen_perc <= Settings.ResourceDangerThreshold && vd.msg_oxygen < 2)
                    {
                        if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH)
                        {
                            Message.Post(Severity.danger, VesselEvent.oxygen, v);
                        }
                        vd.msg_oxygen = 2;
                    }
                    else if (oxygen_perc <= Settings.ResourceWarningThreshold && vd.msg_oxygen < 1)
                    {
                        if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH)
                        {
                            Message.Post(Severity.warning, VesselEvent.oxygen, v);
                        }
                        vd.msg_oxygen = 1;
                    }
                    else if (oxygen_perc > Settings.ResourceWarningThreshold && vd.msg_oxygen > 0)
                    {
                        if (vd.cfg_supply == 1 && v.situation != Vessel.Situations.PRELAUNCH)
                        {
                            Message.Post(Severity.relax, VesselEvent.oxygen, v);
                        }
                        vd.msg_oxygen = 0;
                    }
                }
            }
        }
예제 #23
0
        void toEVA(GameEvents.FromToAction <Part, Part> data)
        {
            // determine if inside breathable atmosphere
            bool breathable = LifeSupport.BreathableAtmosphere(data.from.vessel);

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

            // add resource definitions to EVA vessel part
            Lib.SetupResource(data.to, "ElectricCharge", 0.0, Settings.ElectricChargeOnEVA);
            if (!breathable)
            {
                Lib.SetupResource(data.to, "Oxygen", 0.0, Settings.OxygenOnEVA);
            }


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

            // determine how much ElectricCharge to get
            // note: never more that the 'share' of this kerbal
            // note: always keep half the ec in the vessel
            double ec = Math.Min(Lib.GetResourceAmount(data.from.vessel, "ElectricCharge") / (tot_crew * 2.0), Settings.ElectricChargeOnEVA);


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

            // transfer monoprop
            data.to.RequestResource("EVA Propellant", -data.from.RequestResource("MonoPropellant", monoprop));

            // transfer ec
            data.to.RequestResource("ElectricCharge", -data.from.RequestResource("ElectricCharge", ec));


            // if outside breathable atmosphere
            if (!breathable)
            {
                // determine how much Oxygen to get
                // note: never more that the 'share' of this kerbal
                double oxygen = Math.Min(Lib.GetResourceAmount(data.from.vessel, "Oxygen") / tot_crew, Settings.OxygenOnEVA);

                // transfer oxygen
                data.to.RequestResource("Oxygen", -data.from.RequestResource("Oxygen", oxygen));
            }


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

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

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

            // remember if the kerbal has an helmet in the EVA module
            data.to.FindModuleImplementing <EVA>().has_helmet = !breathable;


            // show warning if there isn't monoprop in the eva suit
            if (monoprop <= double.Epsilon && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger, "There isn't any <b>MonoPropellant</b> in the EVA suit", "Don't let the ladder go!");
            }
        }
예제 #24
0
  public static food_data analyze_food(List<Part> parts, environment_data env, crew_data crew)
  {
    // store data
    food_data food = new food_data();

    // calculate food consumed
    food.consumed = (double)crew.count * Settings.FoodPerMeal / Settings.MealFrequency;

    // deduce waste produced by the crew per-second
    double simulated_waste = food.consumed;

    // scan the parts
    foreach(Part p in parts)
    {
      // accumulate food storage
      food.storage += Lib.GetResourceAmount(p, "Food");

      // for each module
      foreach(PartModule m in p.Modules)
      {
        // greenhouse
        if (m.moduleName == "Greenhouse")
        {
          Greenhouse mm = (Greenhouse)m;

          // calculate natural lighting
          double natural_lighting = Greenhouse.NaturalLighting(env.sun_dist);

          // calculate ec consumed
          food.greenhouse_cost += mm.ec_rate * mm.lamps;

          // calculate lighting
          double lighting = natural_lighting * (mm.door_opened ? 1.0 : 0.0) + mm.lamps * (mm.door_opened ? 1.0 : 1.0 + Settings.GreenhouseDoorBonus);

          // calculate waste used
          double waste_used = Math.Min(simulated_waste, mm.waste_rate);
          double waste_perc = waste_used / mm.waste_rate;
          simulated_waste -= waste_used;

          // calculate growth bonus
          double growth_bonus = 0.0;
          growth_bonus += Settings.GreenhouseSoilBonus * (env.landed ? 1.0 : 0.0);
          growth_bonus += Settings.GreenhouseWasteBonus * waste_perc;

          // calculate growth factor
          double growth_factor = (mm.growth_rate * (1.0 + growth_bonus)) * lighting;

          // calculate food cultivated
          food.cultivated += mm.harvest_size * growth_factor;

          // calculate time-to-harvest
          if (growth_factor > double.Epsilon)
          {
            food.cultivated_tooltip += (food.cultivated_tooltip.Length > 0 ? "\n" : "")
              + "Time-to-harvest: <b>" + Lib.HumanReadableDuration(1.0 / growth_factor) + "</b>";
          }
        }
      }
    }

    // calculate life expectancy
    food.life_expectancy = food.storage / Math.Max(food.consumed - food.cultivated, 0.0);

    // add formatting to tooltip
    if (food.cultivated_tooltip.Length > 0) food.cultivated_tooltip = "<i>" + food.cultivated_tooltip + "</i>";

    // return data
    return food;
  }
예제 #25
0
 // return percentage of radiations blocked by shielding
 public static double Shielding(Vessel v)
 {
     return(Shielding(Lib.GetResourceAmount(v, "Shielding"), Lib.GetResourceCapacity(v, "Shielding")));
 }