예제 #1
0
        private void ExecuteRecipe(double k, VesselResources resources, double elapsed_s, ResourceRecipe recipe)
        {
            // only execute processes if necessary
            if (Math.Abs(k) < double.Epsilon)
            {
                return;
            }

            foreach (var p in inputs)
            {
                recipe.AddInput(p.Key, p.Value * k * elapsed_s);
            }
            foreach (var p in outputs)
            {
                recipe.AddOutput(p.Key, p.Value * k * elapsed_s, dump.Check(p.Key));
            }
            foreach (var p in cures)
            {
                // TODO this assumes that the cure modifies always put the resource first
                // works: modifier = _SickbayRDU,zerog works
                // fails: modifier = zerog,_SickbayRDU
                recipe.AddCure(p.Key, p.Value * k * elapsed_s, modifiers[0]);
            }
            resources.AddRecipe(recipe);
        }
예제 #2
0
        static void ProcessConverter(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceConverter converter, VesselResources resources, double elapsed_s)
        {
            // note: ignore stock temperature mechanic of converters
            // note: ignore auto shutdown
            // note: non-mandatory resources 'dynamically scale the ratios', that is exactly what mandatory resources do too (DERP ALERT)
            // note: 'undo' stock behavior by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // determine if vessel is full of all output resources
                // note: comparing against previous amount
                bool full = true;
                foreach (var or in converter.outputList)
                {
                    ResourceInfo res = resources.GetResource(v, or.ResourceName);
                    full &= (res.Level >= converter.FillAmount - double.Epsilon);
                }

                // if not full
                if (!full)
                {
                    // deduce crew bonus
                    int exp_level = -1;
                    if (converter.UseSpecialistBonus)
                    {
                        foreach (ProtoCrewMember c in Lib.CrewList(v))
                        {
                            if (c.experienceTrait.Effects.Find(k => k.Name == converter.ExperienceEffect) != null)
                            {
                                exp_level = Math.Max(exp_level, c.experienceLevel);
                            }
                        }
                    }
                    double exp_bonus = exp_level < 0
                                          ? converter.EfficiencyBonus * converter.SpecialistBonusBase
                                          : converter.EfficiencyBonus * (converter.SpecialistBonusBase + (converter.SpecialistEfficiencyFactor * (exp_level + 1)));

                    // create and commit recipe
                    ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.StockConverter);
                    foreach (var ir in converter.inputList)
                    {
                        recipe.AddInput(ir.ResourceName, ir.Ratio * exp_bonus * elapsed_s);
                    }
                    foreach (var or in converter.outputList)
                    {
                        recipe.AddOutput(or.ResourceName, or.Ratio * exp_bonus * elapsed_s, or.DumpExcess);
                    }
                    resources.AddRecipe(recipe);
                }

                // undo stock behavior by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
예제 #3
0
        private static void ResourceUpdate(Vessel v, Harvester harvester, double min_abundance, double elapsed_s)
        {
            double abundance = SampleAbundance(v, harvester);

            if (abundance > min_abundance)
            {
                ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.Harvester);
                recipe.AddInput("ElectricCharge", harvester.ec_rate * elapsed_s);
                recipe.AddOutput(
                    harvester.resource,
                    Harvester.AdjustedRate(harvester, engineer_cs, Lib.CrewList(v), abundance) * elapsed_s,
                    dump: false);
                ResourceCache.AddRecipe(v, recipe);
            }
        }
예제 #4
0
 static void ProcessGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleGenerator generator, VesselResources resources, double elapsed_s)
 {
     // if active
     if (Lib.Proto.GetBool(m, "generatorIsActive"))
     {
         // create and commit recipe
         ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.StockConverter);
         foreach (ModuleResource ir in generator.resHandler.inputResources)
         {
             recipe.AddInput(ir.name, ir.rate * elapsed_s);
         }
         foreach (ModuleResource or in generator.resHandler.outputResources)
         {
             recipe.AddOutput(or.name, or.rate * elapsed_s, true);
         }
         resources.AddRecipe(recipe);
     }
 }
예제 #5
0
		private static void ResourceUpdate(Vessel v, Harvester harvester, double min_abundance, double elapsed_s)
		{
			double abundance = SampleAbundance(v, harvester);
			if (abundance > min_abundance)
			{
				double rate = harvester.rate;

				// Bonus(..., -2): a level 0 engineer will alreaday add 2 bonus points jsut because he's there,
				// regardless of level. efficiency will raise further with higher levels.
				int bonus = engineer_cs.Bonus(v, -2);
				double crew_gain = 1 + bonus * Settings.HarvesterCrewLevelBonus;
				crew_gain = Lib.Clamp(crew_gain, 1, Settings.MaxHarvesterBonus);
				rate *= crew_gain;

				ResourceRecipe recipe = new ResourceRecipe("harvester");
				recipe.AddInput("ElectricCharge", harvester.ec_rate * elapsed_s);
				recipe.AddOutput(harvester.resource, harvester.rate * (abundance/harvester.abundance_rate) * elapsed_s, false);
				ResourceCache.AddRecipe(v, recipe);
			}
		}
예제 #6
0
        public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Greenhouse g,
                                            VesselData vd, VesselResources resources, double elapsed_s)
        {
            // get protomodule data
            bool   active = Lib.Proto.GetBool(m, "active");
            double growth = Lib.Proto.GetDouble(m, "growth");

            // if enabled and not ready for harvest
            if (active && growth < 0.99)
            {
                // get resource handler
                ResourceInfo ec = resources.GetResource(v, "ElectricCharge");

                // calculate natural and artificial lighting
                double natural    = vd.EnvSolarFluxTotal;
                double artificial = Math.Max(g.light_tolerance - natural, 0.0);

                // consume EC for the lamps, scaled by artificial light intensity
                if (artificial > double.Epsilon)
                {
                    ec.Consume(g.ec_rate * (artificial / g.light_tolerance) * elapsed_s, ResourceBroker.Greenhouse);
                }

                // reset artificial lighting if there is no ec left
                // note: comparing against amount in previous simulation step
                if (ec.Amount <= double.Epsilon)
                {
                    artificial = 0.0;
                }

                // execute recipe
                ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.Greenhouse);
                foreach (ModuleResource input in g.resHandler.inputResources)                 //recipe.Input(input.name, input.rate * elapsed_s);
                {
                    // WasteAtmosphere is primary combined input
                    if (g.WACO2 && input.name == "WasteAtmosphere")
                    {
                        recipe.AddInput(input.name, vd.EnvBreathable ? 0.0 : input.rate * elapsed_s, "CarbonDioxide");
                    }
                    // CarbonDioxide is secondary combined input
                    else if (g.WACO2 && input.name == "CarbonDioxide")
                    {
                        recipe.AddInput(input.name, vd.EnvBreathable ? 0.0 : input.rate * elapsed_s, "");
                    }
                    // if atmosphere is breathable disable WasteAtmosphere / CO2
                    else if (!g.WACO2 && (input.name == "CarbonDioxide" || input.name == "WasteAtmosphere"))
                    {
                        recipe.AddInput(input.name, vd.EnvBreathable ? 0.0 : input.rate, "");
                    }
                    else
                    {
                        recipe.AddInput(input.name, input.rate * elapsed_s);
                    }
                }
                foreach (ModuleResource output in g.resHandler.outputResources)
                {
                    // if atmosphere is breathable disable Oxygen
                    if (output.name == "Oxygen")
                    {
                        recipe.AddOutput(output.name, vd.EnvBreathable ? 0.0 : output.rate * elapsed_s, true);
                    }
                    else
                    {
                        recipe.AddOutput(output.name, output.rate * elapsed_s, true);
                    }
                }
                resources.AddRecipe(recipe);

                // determine environment conditions
                bool lighting  = natural + artificial >= g.light_tolerance;
                bool pressure  = g.pressure_tolerance <= 0 || vd.Pressure >= g.pressure_tolerance;
                bool radiation = g.radiation_tolerance <= 0 || vd.EnvRadiation * (1.0 - vd.Shielding) < g.radiation_tolerance;

                // determine inputs conditions
                // note: comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                bool   dis_WACO2   = false;
                foreach (ModuleResource input in g.resHandler.inputResources)
                {
                    // combine WasteAtmosphere and CO2 if both exist
                    if (input.name == "WasteAtmosphere" || input.name == "CarbonDioxide")
                    {
                        if (dis_WACO2 || vd.EnvBreathable)
                        {
                            continue;                                                           // skip if already checked or atmosphere is breathable
                        }
                        if (g.WACO2)
                        {
                            if (resources.GetResource(v, "WasteAtmosphere").Amount <= double.Epsilon && resources.GetResource(v, "CarbonDioxide").Amount <= double.Epsilon)
                            {
                                inputs      = false;
                                missing_res = "CarbonDioxide";
                                break;
                            }
                            dis_WACO2 = true;
                            continue;
                        }
                    }
                    if (resources.GetResource(v, input.name).Amount <= double.Epsilon)
                    {
                        inputs      = false;
                        missing_res = input.name;
                        break;
                    }
                }

                // if growing
                if (lighting && pressure && radiation && inputs)
                {
                    // increase growth
                    growth += g.crop_rate * elapsed_s;
                    growth  = Math.Min(growth, 1.0);

                    // notify the user when crop can be harvested
                    if (growth >= 0.99)
                    {
                        Message.Post(Local.harvestedready_msg.Format("<b>" + v.vesselName + "</b>"));                        //Lib.BuildString("On <<1>> the crop is ready to be harvested")
                        growth = 1.0;
                    }
                }

                // update time-to-harvest
                double tta = (1.0 - growth) / g.crop_rate;

                // update issues
                string issue =
                    !inputs?Lib.BuildString(Local.Greenhouse_resoucesmissing.Format(missing_res)) //"missing ", missing_res
                        : !lighting ? Local.Greenhouse_issue1                                     //"insufficient lighting"
                                : !pressure ? Local.Greenhouse_issue2                             //"insufficient pressure"
                                : !radiation ? Local.Greenhouse_issue3                            //"excessive radiation"
                                : string.Empty;

                // update protomodule data
                Lib.Proto.Set(m, "natural", natural);
                Lib.Proto.Set(m, "artificial", artificial);
                Lib.Proto.Set(m, "tta", tta);
                Lib.Proto.Set(m, "issue", issue);
                Lib.Proto.Set(m, "growth", growth);
            }
        }
예제 #7
0
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

            // if enabled and not ready for harvest
            if (active && growth < 0.99)
            {
                // get vessel info from the cache
                // - if the vessel is not valid (eg: flagged as debris) then solar flux will be 0 and landed false (but that's okay)
                VesselData vd = vessel.KerbalismData();

                // get resource cache
                VesselResources resources = ResourceCache.Get(vessel);
                ResourceInfo    ec        = resources.GetResource(vessel, "ElectricCharge");

                // deal with corner cases when greenhouse is assembled using KIS
                if (double.IsNaN(growth) || double.IsInfinity(growth))
                {
                    growth = 0.0;
                }

                // calculate natural and artificial lighting
                natural    = vd.EnvSolarFluxTotal;
                artificial = Math.Max(light_tolerance - natural, 0.0);

                // consume EC for the lamps, scaled by artificial light intensity
                if (artificial > double.Epsilon)
                {
                    ec.Consume(ec_rate * (artificial / light_tolerance) * Kerbalism.elapsed_s, ResourceBroker.Greenhouse);
                }

                // reset artificial lighting if there is no ec left
                // - comparing against amount in previous simulation step
                if (ec.Amount <= double.Epsilon)
                {
                    artificial = 0.0;
                }

                // execute recipe
                ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.Greenhouse);
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    // WasteAtmosphere is primary combined input
                    if (WACO2 && input.name == "WasteAtmosphere")
                    {
                        recipe.AddInput(input.name, vd.EnvBreathable ? 0.0 : input.rate * Kerbalism.elapsed_s, "CarbonDioxide");
                    }
                    // CarbonDioxide is secondary combined input
                    else if (WACO2 && input.name == "CarbonDioxide")
                    {
                        recipe.AddInput(input.name, vd.EnvBreathable ? 0.0 : input.rate * Kerbalism.elapsed_s, "");
                    }
                    // if atmosphere is breathable disable WasteAtmosphere / CO2
                    else if (!WACO2 && (input.name == "CarbonDioxide" || input.name == "WasteAtmosphere"))
                    {
                        recipe.AddInput(input.name, vd.EnvBreathable ? 0.0 : input.rate, "");
                    }
                    else
                    {
                        recipe.AddInput(input.name, input.rate * Kerbalism.elapsed_s);
                    }
                }
                foreach (ModuleResource output in resHandler.outputResources)
                {
                    // if atmosphere is breathable disable Oxygen
                    if (output.name == "Oxygen")
                    {
                        recipe.AddOutput(output.name, vd.EnvBreathable ? 0.0 : output.rate * Kerbalism.elapsed_s, true);
                    }
                    else
                    {
                        recipe.AddOutput(output.name, output.rate * Kerbalism.elapsed_s, true);
                    }
                }
                resources.AddRecipe(recipe);

                // determine environment conditions
                bool lighting  = natural + artificial >= light_tolerance;
                bool pressure  = pressure_tolerance <= double.Epsilon || vd.Pressure >= pressure_tolerance;
                bool radiation = radiation_tolerance <= double.Epsilon || (1.0 - vd.Shielding) * vd.EnvHabitatRadiation < radiation_tolerance;

                // determine input resources conditions
                // - comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                bool   dis_WACO2   = false;
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    // combine WasteAtmosphere and CO2 if both exist
                    if (input.name == "WasteAtmosphere" || input.name == "CarbonDioxide")
                    {
                        if (dis_WACO2 || vd.EnvBreathable)
                        {
                            continue;                                                           // skip if already checked or atmosphere is breathable
                        }
                        if (WACO2)
                        {
                            if (resources.GetResource(vessel, "WasteAtmosphere").Amount <= double.Epsilon && resources.GetResource(vessel, "CarbonDioxide").Amount <= double.Epsilon)
                            {
                                inputs      = false;
                                missing_res = "CarbonDioxide";
                                break;
                            }
                            dis_WACO2 = true;
                            continue;
                        }
                    }
                    if (resources.GetResource(vessel, input.name).Amount <= double.Epsilon)
                    {
                        inputs      = false;
                        missing_res = input.name;
                        break;
                    }
                }

                // if growing
                if (lighting && pressure && radiation && inputs)
                {
                    // increase growth
                    growth += crop_rate * Kerbalism.elapsed_s;
                    growth  = Math.Min(growth, 1.0);

                    // notify the user when crop can be harvested
                    if (growth >= 0.99)
                    {
                        Message.Post(Local.harvestedready_msg.Format("<b>" + vessel.vesselName + "</b>"));                        //Lib.BuildString("On <<1>> the crop is ready to be harvested")
                        growth = 1.0;
                    }
                }

                // update time-to-harvest
                tta = (1.0 - growth) / crop_rate;

                // update issues
                issue =
                    !inputs?Lib.BuildString(Local.Greenhouse_resoucesmissing.Format(missing_res)) //"missing <<1>>"
                        : !lighting ? Local.Greenhouse_issue1                                     //"insufficient lighting"
                                : !pressure ? Local.Greenhouse_issue2                             //"insufficient pressure"
                                : !radiation ? Local.Greenhouse_issue3                            //"excessive radiation"
                                : string.Empty;
            }
        }
예제 #8
0
        public void Execute(Vessel v, VesselData vd, VesselResources resources, double elapsed_s)
        {
            // store list of crew to kill
            List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>();

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

            // determine message variant
            uint variant = vd.EnvTemperature < Settings.LifeSupportSurvivalTemperature ? 0 : 1u;

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

            bool lifetime_enabled = PreferencesRadiation.Instance.lifetime;

            // 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);
                rd.lifetime = lifetime_enabled && lifetime;

                // influence consumption by elapsed time
                double step = elapsed_s;

                // if interval-based
                if (interval > 0.0)
                {
                    // accumulate time
                    rd.time_since += elapsed_s;

                    // determine number of intervals that has passed (can be 2 or more if elapsed_s > interval * 2)
                    step = Math.Floor(rd.time_since / interval);

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

                // if there is a resource specified
                if (res != null && rate > double.Epsilon)
                {
                    // get rate including per-kerbal variance
                    double resRate =
                        rate                                                        // consumption rate
                        * Variance(name, c, individuality)                          // kerbal-specific variance
                        * k;                                                        // product of environment modifiers

                    // determine amount of resource to consume

                    double required = resRate * step;                           // seconds elapsed or interval amount

                    // remember if a meal is consumed/produced in this simulation step
                    if (interval > 0.0)
                    {
                        double ratePerStep = resRate / interval;
                        res.UpdateIntervalRule(-required, -ratePerStep, name);
                        if (output.Length > 0)
                        {
                            ResourceCache.GetResource(v, output).UpdateIntervalRule(required * ratio, ratePerStep * ratio, name);
                        }
                    }

                    // if continuous, or if one or more intervals elapsed
                    if (step > 0.0)
                    {
                        // if there is no output
                        if (output.Length == 0)
                        {
                            // simply consume (that is faster)
                            res.Consume(required, name);
                        }
                        // if there is an output
                        else
                        {
                            // transform input into output resource
                            // - rules always dump excess overboard (because it is waste)
                            ResourceRecipe recipe = new ResourceRecipe(name);
                            recipe.AddInput(input, required);
                            recipe.AddOutput(output, required * ratio, true);
                            resources.AddRecipe(recipe);
                        }
                    }
                }

                // if continuous, or if one or more intervals elapsed
                if (step > 0.0)
                {
                    // degenerate:
                    // - if the environment modifier is not telling to reset (by being zero)
                    // - if this rule is resource-less, or if there was not enough resource in the vessel
                    if (k > 0.0 && (input.Length == 0 || res.Amount <= double.Epsilon))
                    {
                        rd.problem += degeneration                                   // degeneration rate per-second or per-interval
                                      * k                                            // product of environment modifiers
                                      * step                                         // seconds elapsed or by number of steps
                                      * Variance(name, c, variance);                 // kerbal-specific variance
                    }
                    // else slowly recover
                    else
                    {
                        rd.problem *= 1.0 / (1.0 + Math.Max(interval, 1.0) * step * 0.002);
                    }
                }

                bool do_breakdown = false;

                if (breakdown)
                {
                    // don't do breakdowns and don't show stress message if disabled
                    if (!PreferencesComfort.Instance.stressBreakdowns)
                    {
                        return;
                    }

                    // stress level
                    double breakdown_probability = rd.problem / warning_threshold;
                    breakdown_probability = Lib.Clamp(breakdown_probability, 0.0, 1.0);

                    // use the stupidity of a kerbal.
                    // however, nobody is perfect - not even a kerbal with a stupidity of 0.
                    breakdown_probability *= c.stupidity * 0.6 + 0.4;

                    // apply the weekly error rate
                    breakdown_probability *= PreferencesComfort.Instance.stressBreakdownRate;

                    // now we have the probability for one failure per week, based on the
                    // individual stupidity and stress level of the kerbal.

                    breakdown_probability = (breakdown_probability * elapsed_s) / (Lib.DaysInYear * Lib.HoursInDay * 3600);
                    if (breakdown_probability > Lib.RandomDouble())
                    {
                        do_breakdown = true;

                        // we're stressed out and just made a major mistake, this further increases the stress level...
                        rd.problem += warning_threshold * 0.05;                         // add 5% of the warning treshold to current stress level
                    }
                }

                // kill kerbal if necessary
                if (rd.problem >= fatal_threshold)
                {
#if DEBUG || DEVBUILD
                    Lib.Log("Rule " + name + " kills " + c.name + " at " + rd.problem + " " + degeneration + "/" + k + "/" + step + "/" + Variance(name, c, variance));
#endif
                    if (fatal_message.Length > 0)
                    {
                        Message.Post(breakdown ? Severity.breakdown : Severity.fatality, Lib.ExpandMsg(fatal_message, v, c, variant));
                    }

                    if (breakdown)
                    {
                        do_breakdown = true;

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

                        // make sure next danger message 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;
                }

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

            // execute the deferred kills
            foreach (ProtoCrewMember c in deferred_kills)
            {
                Misc.Kill(v, c);
            }
        }
예제 #9
0
        // Doesn't work since squad refactored the ModuleAsteroidInfo / ModuleAsteroidResource for Comets (in 1.10 ?), and was probably not working even before that.
        static void ProcessAsteroidDrill(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleAsteroidDrill asteroid_drill, VesselResources resources, double elapsed_s)
        {
            // note: untested
            // note: ignore stock temperature mechanic of asteroid drills
            // note: ignore auto shutdown
            // note: 'undo' stock behavior by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // get asteroid data
                ProtoPartModuleSnapshot asteroid_info     = null;
                ProtoPartModuleSnapshot asteroid_resource = null;
                foreach (ProtoPartSnapshot pp in v.protoVessel.protoPartSnapshots)
                {
                    if (asteroid_info == null)
                    {
                        asteroid_info = pp.modules.Find(k => k.moduleName == "ModuleAsteroidInfo");
                    }
                    if (asteroid_resource == null)
                    {
                        asteroid_resource = pp.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 = Lib.Proto.GetDouble(asteroid_info, "massThresholdVal");
                    double mass           = Lib.Proto.GetDouble(asteroid_info, "currentMassVal");
                    double abundance      = Lib.Proto.GetDouble(asteroid_resource, "abundance");
                    string res_name       = Lib.Proto.GetString(asteroid_resource, "resourceName");
                    double res_density    = PartResourceLibrary.Instance.GetDefinition(res_name).density;

                    // if asteroid isn't depleted
                    if (mass > mass_threshold && abundance > double.Epsilon)
                    {
                        // deduce crew bonus
                        int exp_level = -1;
                        if (asteroid_drill.UseSpecialistBonus)
                        {
                            foreach (ProtoCrewMember c in Lib.CrewList(v))
                            {
                                if (c.experienceTrait.Effects.Find(k => k.Name == asteroid_drill.ExperienceEffect) != null)
                                {
                                    exp_level = Math.Max(exp_level, c.experienceLevel);
                                }
                            }
                        }
                        double exp_bonus = exp_level < 0
                                                ? asteroid_drill.EfficiencyBonus * asteroid_drill.SpecialistBonusBase
                                                : asteroid_drill.EfficiencyBonus * (asteroid_drill.SpecialistBonusBase + (asteroid_drill.SpecialistEfficiencyFactor * (exp_level + 1)));

                        // determine resource extracted
                        double res_amount = abundance * asteroid_drill.Efficiency * exp_bonus * elapsed_s;

                        // transform EC into mined resource
                        ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.StockDrill);
                        recipe.AddInput("ElectricCharge", asteroid_drill.PowerConsumption * elapsed_s);
                        recipe.AddOutput(res_name, res_amount, true);
                        resources.AddRecipe(recipe);

                        // if there was ec
                        // note: comparing against amount in previous simulation step
                        if (resources.GetResource(v, "ElectricCharge").Amount > double.Epsilon)
                        {
                            // consume asteroid mass
                            Lib.Proto.Set(asteroid_info, "currentMassVal", (mass - res_density * res_amount));
                        }
                    }
                }

                // undo stock behavior by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
예제 #10
0
        static void ProcessDrill(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceHarvester harvester, VesselResources resources, double elapsed_s)
        {
            // note: ignore stock temperature mechanic of harvesters
            // note: ignore auto shutdown
            // note: ignore depletion (stock seem to do the same)
            // note: 'undo' stock behavior by forcing lastUpdateTime to now (to minimize overlapping calculations from this and stock post-facto simulation)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // do nothing if full
                // note: comparing against previous amount
                if (resources.GetResource(v, harvester.ResourceName).Level < harvester.FillAmount - double.Epsilon)
                {
                    // deduce crew bonus
                    int exp_level = -1;
                    if (harvester.UseSpecialistBonus)
                    {
                        foreach (ProtoCrewMember c in Lib.CrewList(v))
                        {
                            if (c.experienceTrait.Effects.Find(k => k.Name == harvester.ExperienceEffect) != null)
                            {
                                exp_level = Math.Max(exp_level, c.experienceLevel);
                            }
                        }
                    }
                    double exp_bonus = exp_level < 0
                                          ? harvester.EfficiencyBonus * harvester.SpecialistBonusBase
                                          : harvester.EfficiencyBonus * (harvester.SpecialistBonusBase + (harvester.SpecialistEfficiencyFactor * (exp_level + 1)));

                    // detect amount of ore in the ground
                    AbundanceRequest request = new AbundanceRequest
                    {
                        Altitude     = v.altitude,
                        BodyId       = v.mainBody.flightGlobalsIndex,
                        CheckForLock = false,
                        Latitude     = v.latitude,
                        Longitude    = v.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)
                    {
                        // create and commit recipe
                        ResourceRecipe recipe = new ResourceRecipe(ResourceBroker.StockDrill);
                        foreach (var ir in harvester.inputList)
                        {
                            recipe.AddInput(ir.ResourceName, ir.Ratio * elapsed_s);
                        }
                        recipe.AddOutput(harvester.ResourceName, abundance * harvester.Efficiency * exp_bonus * elapsed_s, true);
                        resources.AddRecipe(recipe);
                    }
                }

                // undo stock behavior by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }