Beispiel #1
0
        // execute the recipe
        public bool Execute(Vessel v, vessel_resources resources)
        {
            // determine worst input ratio
            // - pure input recipes can just underflow
            double worst_input = left;

            if (outputs.Count > 0)
            {
                for (int i = 0; i < inputs.Count; ++i)
                {
                    entry         e   = inputs[i];
                    resource_info res = resources.Info(v, e.name);
                    worst_input = Lib.Clamp((res.amount + res.deferred) * e.inv_quantity, 0.0, worst_input);
                }
            }

            // determine worst output ratio
            // - pure output recipes can just overflow
            double worst_output = left;

            if (inputs.Count > 0)
            {
                for (int i = 0; i < outputs.Count; ++i)
                {
                    entry e = outputs[i];
                    if (!e.dump)                     // ignore outputs that can dump overboard
                    {
                        resource_info res = resources.Info(v, e.name);
                        worst_output = Lib.Clamp((res.capacity - (res.amount + res.deferred)) * e.inv_quantity, 0.0, worst_output);
                    }
                }
            }

            // determine worst-io
            double worst_io = Math.Min(worst_input, worst_output);

            // consume inputs
            for (int i = 0; i < inputs.Count; ++i)
            {
                entry e = inputs[i];
                resources.Consume(v, e.name, e.quantity * worst_io);
            }

            // produce outputs
            for (int i = 0; i < outputs.Count; ++i)
            {
                entry e = outputs[i];
                resources.Produce(v, e.name, e.quantity * worst_io);
            }

            // update amount left to execute
            left -= worst_io;

            // the recipe was executed, at least partially
            return(worst_io > double.Epsilon);
        }
Beispiel #2
0
  // implement scrubber mechanics
  public void FixedUpdate()
  {
    // do nothing in the editor
    if (HighLogic.LoadedSceneIsEditor) return;

    // do nothing until tech tree is ready
    if (!Lib.TechReady()) return;

    // deduce quality from technological level if necessary
    if (efficiency <= double.Epsilon) efficiency = DeduceEfficiency();

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

    // do nothing if vessel is invalid
    if (!vi.is_valid) return;

    // get resource cache
    vessel_resources resources = ResourceCache.Get(vessel);

    // get elapsed time
    double elapsed_s = Kerbalism.elapsed_s * vi.time_dilation;

    // if inside breathable atmosphere
    if (vi.breathable)
    {
      // produce oxygen from the intake
      resources.Produce(vessel, resource_name, intake_rate * elapsed_s);

      // set status
      Status = "Intake";
    }
    // if outside breathable atmosphere and enabled
    else if (is_enabled)
    {
      // transform waste + ec into resource
      resource_recipe recipe = new resource_recipe(resource_recipe.scrubber_priority);
      recipe.Input(waste_name, co2_rate * elapsed_s);
      recipe.Input("ElectricCharge", ec_rate * elapsed_s);
      recipe.Output(resource_name, co2_rate * co2_ratio * efficiency * elapsed_s);
      resources.Transform(recipe);

      // set status
      Status = "Running";
    }
    // if outside breathable atmosphere and disabled
    else
    {
      // set status
      Status = "Off";
    }

    // add efficiency to status
    Status += Lib.BuildString(" (Efficiency: ", (efficiency * 100.0).ToString("F0"), "%)");
  }
Beispiel #3
0
 // implement scrubber mechanics for unloaded vessels
 public static void BackgroundUpdate(Vessel vessel, ProtoPartModuleSnapshot m, Scrubber scrubber, vessel_info info, vessel_resources resources, double elapsed_s)
 {
   // if inside breathable atmosphere
   if (info.breathable)
   {
     // produce oxygen from the intake
     resources.Produce(vessel, scrubber.resource_name, scrubber.intake_rate * elapsed_s);
   }
   // if outside breathable atmosphere and enabled
   else if (Lib.Proto.GetBool(m, "is_enabled"))
   {
     // transform waste + ec into resource
     resource_recipe recipe = new resource_recipe(resource_recipe.scrubber_priority);
     recipe.Input(scrubber.waste_name, scrubber.co2_rate * elapsed_s);
     recipe.Input("ElectricCharge", scrubber.ec_rate * elapsed_s);
     recipe.Output(scrubber.resource_name, scrubber.co2_rate * scrubber.co2_ratio * Lib.Proto.GetDouble(m, "efficiency", 0.5) * elapsed_s);
     resources.Transform(recipe);
   }
 }
Beispiel #4
0
        public void Execute(Vessel v, vessel_info vi, vessel_resources resources, double elapsed_s)
        {
            // store list of crew to kill
            List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>();

            // get input resource handler
            resource_info res = input.Length > 0 ? resources.Info(v, input) : null;

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

            // get product of all environment modifiers
            double k = Modifiers.evaluate(v, vi, resources, modifiers);

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

                // skip rescue kerbals
                if (kd.rescue)
                {
                    continue;
                }

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

                // get kerbal property data from db
                RuleData rd = kd.Rule(name);

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

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

                    // consume time
                    rd.time_since -= step * interval;

                    // remember if a meal is consumed/produced in this simulation step
                    res.meal_happened |= step > 0.99;
                    if (output.Length > 0)
                    {
                        ResourceCache.Info(v, output).meal_happened |= step > 0.99;
                    }
                }

                // if continuous, or if one or more intervals elapsed
                if (step > double.Epsilon)
                {
                    // if there is a resource specified
                    if (res != null && rate > double.Epsilon)
                    {
                        // determine amount of resource to consume
                        double required = rate                                    // consumption rate
                                          * k                                     // product of environment modifiers
                                          * step;                                 // seconds elapsed or number of steps

                        // if there is no output
                        if (output.Length == 0)
                        {
                            // simply consume (that is faster)
                            res.Consume(required);
                        }
                        // if there is an output and output_only is false
                        else if (!output_only)
                        {
                            // transform input into output resource
                            // - rules always dump excess overboard (because it is waste)
                            resource_recipe recipe = new resource_recipe();
                            recipe.Input(input, required);
                            recipe.Output(output, required * ratio, true);
                            resources.Transform(recipe);
                        }
                        // if output_only then do not consume input resource
                        else
                        {
                            // simply produce (that is faster)
                            resources.Produce(v, output, required);
                        }
                    }

                    // degenerate:
                    // - if the environment modifier is not telling to reset (by being zero)
                    // - if the input threshold is reached if used
                    // - if this rule is resource-less, or if there was not enough resource in the vessel
                    if (input_threshold >= double.Epsilon)
                    {
                        if (res.amount >= double.Epsilon && res.capacity >= double.Epsilon)
                        {
                            trigger = res.amount / res.capacity >= input_threshold;
                        }
                        else
                        {
                            trigger = false;
                        }
                    }
                    else
                    {
                        trigger = input.Length == 0 || res.amount <= double.Epsilon;
                    }

                    if (k > 0.0 && trigger)
                    {
                        rd.problem += degeneration                                   // degeneration rate per-second or per-interval
                                      * k                                            // product of environment modifiers
                                      * step                                         // seconds elapsed or by number of steps
                                      * Variance(c, variance);                       // kerbal-specific variance
                    }
                    // else slowly recover
                    else
                    {
                        rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002);
                        rd.problem  = Math.Max(rd.problem, 0.0);
                    }
                }

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

                    if (breakdown)
                    {
                        // trigger breakdown event
                        Misc.Breakdown(v, c);

                        // move back between warning and danger level
                        rd.problem = (warning_threshold + danger_threshold) * 0.5;

                        // make sure next danger messagen is shown
                        rd.message = 1;
                    }
                    else
                    {
                        deferred_kills.Add(c);
                    }
                }
                // show messages
                else if (rd.problem >= danger_threshold && rd.message < 2)
                {
                    if (danger_message.Length > 0)
                    {
                        Message.Post(Severity.danger, Lib.ExpandMsg(danger_message, v, c, variant));
                    }
                    rd.message = 2;
                }
                else if (rd.problem >= warning_threshold && rd.message < 1)
                {
                    if (warning_message.Length > 0)
                    {
                        Message.Post(Severity.warning, Lib.ExpandMsg(warning_message, v, c, variant));
                    }
                    rd.message = 1;
                }
                else if (rd.problem < warning_threshold && rd.message > 0)
                {
                    if (relax_message.Length > 0)
                    {
                        Message.Post(Severity.relax, Lib.ExpandMsg(relax_message, v, c, variant));
                    }
                    rd.message = 0;
                }
            }

            // execute the deferred kills
            foreach (ProtoCrewMember c in deferred_kills)
            {
                Misc.Kill(v, c);
            }
        }
Beispiel #5
0
  // implement greenhouse mechanics for unloaded vessels
  public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Greenhouse greenhouse, vessel_info info, vessel_resources resources, double elapsed_s)
  {
    // get protomodule data
    bool door_opened = Lib.Proto.GetBool(m, "door_opened");
    double growth = Lib.Proto.GetDouble(m, "growth");
    float lamps = Lib.Proto.GetFloat(m, "lamps");
    double lighting = Lib.Proto.GetDouble(m, "lighting");

    // if lamp is on
    if (lamps > float.Epsilon)
    {
      // get resource handler
      resource_info ec = resources.Info(vessel, "ElectricCharge");

      // consume ec
      ec.Consume(greenhouse.ec_rate * lamps * elapsed_s * Reliability.Penalty(p, "Greenhouse", 2.0));

      // shut down the light if there isn't enough ec
      // note: comparing against amount at previous simulation step
      if (ec.amount <= double.Epsilon) lamps = 0.0f;
    }

    // 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 = info.solar_flux / Sim.SolarFluxAtHome() * (door_opened ? 1.0 : 0.0) + lamps;

    // if can use waste, and there is some lighting
    double waste_perc = 0.0;
    if (greenhouse.waste_name.Length > 0 && lighting > double.Epsilon)
    {
      // get resource handler
      resource_info waste = resources.Info(vessel, greenhouse.waste_name);

      // consume waste
      waste.Consume(greenhouse.waste_rate * elapsed_s);

      // determine waste bonus
      // note: comparing against amount from previous simulation step
      waste_perc = Math.Min(waste.amount / greenhouse.waste_rate, 1.0);
    }

    // determine growth bonus
    double growth_bonus = greenhouse.soil_bonus * (info.landed ? 1.0 : 0.0) + greenhouse.waste_bonus * waste_perc;

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

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

      // produce food
      resources.Produce(vessel, greenhouse.resource_name, greenhouse.harvest_size);

      // show a message to the user
      Message.Post(Lib.BuildString("On <color=FFFFFF>", vessel.vesselName, "</color> the crop harvest produced <color=FFFFFF>",
        greenhouse.harvest_size.ToString("F0"), " ", greenhouse.resource_name, "</color>"));

      // record first space harvest
      if (!info.landed && DB.Ready()) DB.Landmarks().space_harvest = 1;
    }

    // store data
    Lib.Proto.Set(m, "growth", growth);
    Lib.Proto.Set(m, "lamps", lamps);
    Lib.Proto.Set(m, "lighting", lighting);
    Lib.Proto.Set(m, "growth_diff", growing);
  }
Beispiel #6
0
  // implement greenhouse mechanics
  public void FixedUpdate()
  {
    // set emissive intensity from lamp tweakable
    if (emissive_object.Length > 0)
    {
      foreach(var rdr in part.GetComponentsInChildren<UnityEngine.Renderer>())
      {
        if (rdr.name == emissive_object) { rdr.material.SetColor("_EmissiveColor", new Color(lamps, lamps, lamps, 1.0f)); break; }
      }
    }

    // do nothing else in the editor
    if (HighLogic.LoadedSceneIsEditor) return;

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

    // do nothing if vessel is invalid
    if (!vi.is_valid) return;

    // get resource cache
    vessel_resources resources = ResourceCache.Get(vessel);

    // get elapsed time
    double elapsed_s = Kerbalism.elapsed_s * vi.time_dilation;

    // when the greenhouse is assembled using KIS, the growth field is set to NaN
    // at that point it remain NaN forever, so we fix it
    // also, a report indicated -infinity in growth
    if (double.IsNaN(growth) || double.IsInfinity(growth)) growth = 0.0;

    // if lamp is on
    if (lamps > float.Epsilon)
    {
      // get resource handler
      resource_info ec = resources.Info(vessel, "ElectricCharge");

      // consume ec
      ec.Consume(ec_rate * lamps * elapsed_s);

      // shut down the light if there isn't enough ec
      // note: comparing against amount at previous simulation step
      if (ec.amount <= double.Epsilon) lamps = 0.0f;
    }

    // determine lighting conditions
    // note: we ignore sun direction for gameplay reasons: else the user must reorient the greenhouse as the planets dance over time
    lighting = vi.solar_flux / Sim.SolarFluxAtHome() * (door_opened ? 1.0 : 0.0) + lamps;

    // if can use waste, and there is some lighting
    double waste_perc = 0.0;
    if (waste_name.Length > 0 && waste_rate > double.Epsilon && lighting > double.Epsilon)
    {
      // get resource handler
      resource_info waste = resources.Info(vessel, waste_name);

      // consume waste
      waste.Consume(waste_rate * elapsed_s);

      // determine waste bonus
      // note: comparing against amount from previous simulation step
      waste_perc = Math.Min(waste.amount / waste_rate, 1.0);
    }

    // determine growth bonus
    double growth_bonus = soil_bonus * (vi.landed ? 1.0 : 0.0) + waste_bonus * waste_perc;

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

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

      // produce food
      resources.Produce(vessel, resource_name, harvest_size);

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

      // record first space harvest
      if (!vi.landed && DB.Ready()) DB.Landmarks().space_harvest = 1;
    }

    // set rmb ui status
    GrowthStatus = Lib.HumanReadablePerc(growth);
    LightStatus = Lib.HumanReadablePerc(lighting);
    WasteStatus = Lib.HumanReadablePerc(waste_perc);
    SoilStatus = vi.landed ? "yes" : "no";
    TTAStatus = Lib.HumanReadableDuration(growing > double.Epsilon ? (1.0 - growth) / growing : 0.0);


    // enable/disable emergency harvest
    Events["EmergencyHarvest"].active = (growth >= 0.5);
  }