Exemple #1
0
        public static double Evaluate(Vessel v, Vessel_Info vi, Vessel_Resources resources, List <string> modifiers)
        {
            double k = 1.0;

            foreach (string mod in modifiers)
            {
                switch (mod)
                {
                case "breathable":
                    k *= vi.breathable ? 0.0 : 1.0;
                    break;

                case "temperature":
                    k *= vi.temp_diff;
                    break;

                case "radiation":
                    k *= vi.radiation;
                    break;

                case "shielding":
                    k *= 1.0 - vi.shielding;
                    break;

                case "volume":
                    k *= vi.volume;
                    break;

                case "surface":
                    k *= vi.surface;
                    break;

                case "living_space":
                    k /= vi.living_space;
                    break;

                case "comfort":
                    k /= vi.comforts.factor;
                    break;

                case "pressure":
                    k *= vi.pressure > Settings.PressureThreshold ? 1.0 : Settings.PressureFactor;
                    break;

                case "poisoning":
                    k *= vi.poisoning > Settings.PoisoningThreshold ? 1.0 : Settings.PoisoningFactor;
                    break;

                case "per_capita":
                    k /= (double)Math.Max(vi.crew_count, 1);
                    break;

                default:
                    k *= resources.Info(v, mod).amount;
                    break;
                }
            }
            return(k);
        }
        // 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);
        }
Exemple #3
0
        // Monitoring all indicator to support life
        public static void Telemetry_Life(this Panel p, Vessel v)
        {
            // avoid corner-case when this is called in a lambda after scene changes
            v = FlightGlobals.FindVessel(v.id);

            // if vessel doesn't exist anymore, leave the panel empty
            if (v == null)
            {
                return;
            }

            // get info from the cache
            Vessel_Info vi = Cache.VesselInfo(v);

            // if not a valid vessel, leave the panel empty
            if (!vi.is_valid)
            {
                return;
            }

            // set metadata
            p.Title(Lib.BuildString(Lib.Ellipsis(v.vesselName, 20), " <color=#cccccc>TELEMETRY</color>"));

            // time-out simulation
            if (p.Timeout(vi))
            {
                return;
            }

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

            // get resources
            Vessel_Resources resources = ResourceCache.Get(v);

            // get crew
            var crew = Lib.CrewList(v);

            // draw the content
            Render_Crew(p, crew);
            Render_Greenhouse(p, vi);
            Render_Supplies(p, v, vi, resources);
            Render_Habitat(p, v, vi);
            Render_Environment(p, v, vi);

            // collapse eva kerbal sections into one
            if (v.isEVA)
            {
                p.Collapse("EVA SUIT");
            }
        }
Exemple #4
0
        public void Execute(Vessel v, VesselData vd, Vessel_Resources resources)
        {
            // get crew
            List <ProtoCrewMember> crew = Lib.CrewList(v);

            // get resource handler
            Resource_Info res = resources.Info(v, resource);

            // get data from db
            SupplyData sd = DB.Vessel(v).Supply(resource);

            // message obey user config
            bool show_msg = resource == "ElectricCharge" ? vd.cfg_ec : vd.cfg_supply;

            // messages are shown only if there is some capacity and the vessel is manned
            // special case: ElectricCharge related messages are shown for unmanned vessels too
            if (res.capacity > double.Epsilon && (crew.Count > 0 || resource == "ElectricCharge"))
            {
                // manned/probe message variant
                uint variant = crew.Count > 0 ? 0 : 1u;

                // manage messages
                if (res.level <= double.Epsilon && sd.message < 2)
                {
                    if (empty_message.Length > 0 && show_msg)
                    {
                        Message.Post(Severity.danger, Lib.ExpandMsg(empty_message, v, null, variant));
                    }
                    sd.message = 2;
                }
                else if (res.level < low_threshold && sd.message < 1)
                {
                    if (low_message.Length > 0 && show_msg)
                    {
                        Message.Post(Severity.warning, Lib.ExpandMsg(low_message, v, null, variant));
                    }
                    sd.message = 1;
                }
                else if (res.level > low_threshold && sd.message > 0)
                {
                    if (refill_message.Length > 0 && show_msg)
                    {
                        Message.Post(Severity.relax, Lib.ExpandMsg(refill_message, v, null, variant));
                    }
                    sd.message = 0;
                }
            }
        }
        // return resource cache for a vessel
        public static Vessel_Resources Get(Vessel v)
        {
            // try to get existing entry if any
            if (entries.TryGetValue(v.id, out Vessel_Resources entry))
            {
                return(entry);
            }

            // create new entry
            entry = new Vessel_Resources();

            // remember new entry
            entries.Add(v.id, entry);

            // return new entry
            return(entry);
        }
        void ToEVA(GameEvents.FromToAction <Part, Part> data)
        {
            // get total crew in the origin vessel
            double tot_crew = (double)Lib.CrewCount(data.from.vessel) + 1.0;

            // get vessel resources handler
            Vessel_Resources resources = ResourceCache.Get(data.from.vessel);

            // setup supply resources capacity in the eva kerbal
            Profile.SetupEva(data.to);

            // for each resource in the kerbal
            for (int i = 0; i < data.to.Resources.Count; ++i)
            {
                // get the resource
                PartResource res = data.to.Resources[i];

                // determine quantity to take
                double quantity = Math.Min(resources.Info(data.from.vessel, res.resourceName).amount / tot_crew, res.maxAmount);

                // remove resource from vessel
                quantity = data.from.RequestResource(res.resourceName, quantity);

                // add resource to eva kerbal
                data.to.RequestResource(res.resourceName, -quantity);
            }

            // show warning if there isn't monoprop in the eva suit
            string prop_name = Lib.EvaPropellantName();

            if (Lib.Amount(data.to, prop_name) <= double.Epsilon && !Lib.Landed(data.from.vessel))
            {
                Message.Post(Severity.danger, Lib.BuildString("There isn't any <b>", prop_name, "</b> in the EVA suit"), "Don't let the ladder go!");
            }

            // turn off headlamp light, to avoid stock bug that show them for a split second when going on eva
            KerbalEVA kerbal = data.to.FindModuleImplementing <KerbalEVA>();

            EVA.HeadLamps(kerbal, false);

            // execute script
            DB.Vessel(data.from.vessel).computer.Execute(data.from.vessel, ScriptType.eva_out);
        }
        public static void Execute(Vessel v, Vessel_Info vi, VesselData vd, Vessel_Resources resources, double elapsed_s)
        {
            // execute all supplies
            foreach (Supply supply in supplies)
            {
                supply.Execute(v, vd, resources);
            }

            // execute all rules
            foreach (Rule rule in rules)
            {
                rule.Execute(v, vi, resources, elapsed_s);
            }

            // execute all processes
            foreach (Process process in processes)
            {
                process.Execute(v, vi, resources, elapsed_s);
            }
        }
        public void Execute(Vessel v, Vessel_Info vi, Vessel_Resources resources, double elapsed_s)
        {
            // evaluate modifiers
            double k = Modifiers.Evaluate(v, vi, resources, modifiers);

            // only execute processes if necessary
            if (k > double.Epsilon)
            {
                // prepare recipe
                Resource_Recipe recipe = new Resource_Recipe();
                foreach (var p in inputs)
                {
                    recipe.Input(p.Key, p.Value * k * elapsed_s);
                }
                foreach (var p in outputs)
                {
                    recipe.Output(p.Key, p.Value * k * elapsed_s, dump.Check(p.Key));
                }
                resources.Transform(recipe);
            }
        }
Exemple #9
0
        static void Render_Supplies(Panel p, Vessel v, Vessel_Info vi, Vessel_Resources resources)
        {
            // for each supply
            int supplies = 0;

            foreach (Supply supply in Profile.supplies)
            {
                // get resource info
                Resource_Info res = resources.Info(v, supply.resource);

                // only show estimate if the resource is present
                if (res.amount <= double.Epsilon)
                {
                    continue;
                }

                // render panel title, if not done already
                if (supplies == 0)
                {
                    p.SetSection("SUPPLIES");
                }

                // rate tooltip
                string rate_tooltip = Math.Abs(res.rate) >= 1e-10 ? Lib.BuildString
                                      (
                    res.rate > 0.0 ? "<color=#00ff00><b>" : "<color=#ff0000><b>",
                    Lib.HumanReadableRate(Math.Abs(res.rate)),
                    "</b></color>"
                                      ) : string.Empty;

                // determine label
                string label = supply.resource == "ElectricCharge"
          ? "battery"
          : Lib.SpacesOnCaps(supply.resource).ToLower();

                // finally, render resource supply
                p.SetContent(label, Lib.HumanReadableDuration(res.Depletion(vi.crew_count)), rate_tooltip);
                ++supplies;
            }
        }
        bool Render_Vessel(Panel p, Vessel v)
        {
            // get vessel info
            Vessel_Info vi = Cache.VesselInfo(v);

            // skip invalid vessels
            if (!vi.is_valid)
            {
                return(false);
            }

            if (!Lib.IsVessel(v))
            {
                return(false);
            }

            // get data from db
            VesselData vd = DB.Vessel(v);

            // determine if filter must be shown
            show_filter |= vd.group.Length > 0 && vd.group != "NONE";

            // skip filtered vessels
            if (Filtered() && vd.group != filter)
            {
                return(false);
            }

            // get resource handler
            Vessel_Resources resources = ResourceCache.Get(v);

            // get vessel crew
            List <ProtoCrewMember> crew = Lib.CrewList(v);

            // get vessel name
            string vessel_name = v.isEVA ? crew[0].name : v.vesselName;

            // get body name
            string body_name = v.mainBody.name.ToUpper();

            // render entry
            p.SetHeader
            (
                Lib.BuildString("<b>", Lib.Ellipsis(vessel_name, 20), "</b> <size=9><color=#cccccc>", Lib.Ellipsis(body_name, 8), "</color></size>"),
                string.Empty,
                () => { selected_id = selected_id != v.id ? v.id : Guid.Empty; }
            );

            // problem indicator
            Indicator_Problems(p, v, vi, crew);

            // battery indicator
            Indicator_EC(p, v, vi);

            // supply indicator
            if (Features.Supplies)
            {
                Indicator_Supplies(p, v, vi);
            }

            // reliability indicator
            if (Features.Reliability)
            {
                Indicator_Reliability(p, v, vi);
            }

            // signal indicator
            if (Features.Signal || Features.KCommNet || RemoteTech.Enabled())
            {
                Indicator_Signal(p, v, vi);
            }

            // done
            return(true);
        }
Exemple #11
0
        static void ProcessCryoTank(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule simple_boiloff, Vessel_Resources resources, double elapsed_s)
        {
            // note: cryotank module already does a post-facto simulation of background boiling, and we could use that for the boiling
            // however, it also does simulate the ec consumption that way, so we have to disable the post-facto simulation

            // get fuel name
            string fuel_name = Lib.ReflectionValue <string>(simple_boiloff, "FuelName");

            // get resource handlers
            Resource_Info ec   = resources.Info(v, "ElectricCharge");
            Resource_Info fuel = resources.Info(v, fuel_name);

            // if there is some fuel
            // note: comparing against amount in previous simulation step
            if (fuel.amount > double.Epsilon)
            {
                // get capacity in the part
                double capacity = p.resources.Find(k => k.resourceName == fuel_name).maxAmount;

                // if cooling is enabled and there was enough ec
                // note: comparing against amount in previous simulation step
                if (Lib.Proto.GetBool(m, "CoolingEnabled") && ec.amount > double.Epsilon)
                {
                    // get cooling ec cost per 1000 units of fuel, per-second
                    double cooling_cost = Lib.ReflectionValue <float>(simple_boiloff, "CoolingCost");

                    // consume ec
                    ec.Consume(cooling_cost * capacity * 0.001 * elapsed_s);
                }
                // if there wasn't ec, or if cooling is disabled
                else
                {
                    // get boiloff rate in proportion to fuel amount, per-second
                    double boiloff_rate = Lib.ReflectionValue <float>(simple_boiloff, "BoiloffRate") * 0.00000277777;

                    // let it boil off
                    fuel.Consume(capacity * (1.0 - Math.Pow(1.0 - boiloff_rate, elapsed_s)));
                }
            }

            // disable post-facto simulation
            Lib.Proto.Set(m, "LastUpdateTime", v.missionTime);
        }
        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
                        else
                        {
                            // 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);
                        }
                    }

                    // 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(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);
            }
        }
Exemple #13
0
 static void ProcessCommand(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleCommand command, Vessel_Resources resources, double elapsed_s)
 {
     // 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 || p.protoModuleCrew.Count > 0)
     {
         // for each input resource
         foreach (ModuleResource ir in command.resHandler.inputResources)
         {
             // consume the resource
             resources.Consume(v, ir.name, ir.rate * elapsed_s);
         }
     }
 }
Exemple #14
0
        void FixedUpdate()
        {
            // remove control locks in any case
            Misc.ClearLocks();

            // do nothing if paused
            if (Lib.IsPaused())
            {
                return;
            }

            // maintain elapsed_s, converting to double only once
            // and detect warp blending
            double fixedDeltaTime = TimeWarp.fixedDeltaTime;

            if (Math.Abs(fixedDeltaTime - elapsed_s) > double.Epsilon)
            {
                warp_blending = 0;
            }
            else
            {
                ++warp_blending;
            }
            elapsed_s = fixedDeltaTime;

            // evict oldest entry from vessel cache
            Cache.Update();

            // store info for oldest unloaded vessel
            double           last_time      = 0.0;
            Vessel           last_v         = null;
            Vessel_Info      last_vi        = null;
            VesselData       last_vd        = null;
            Vessel_Resources last_resources = null;

            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // get vessel info from the cache
                Vessel_Info vi = Cache.VesselInfo(v);

                // set locks for active vessel
                if (v.isActiveVessel)
                {
                    Misc.SetLocks(v, vi);
                }

                // maintain eva dead animation and helmet state
                if (v.loaded && v.isEVA)
                {
                    EVA.Update(v);
                }

                // keep track of rescue mission kerbals, and gift resources to their vessels on discovery
                if (v.loaded && vi.is_vessel)
                {
                    // manage rescue mission mechanics
                    Misc.ManageRescueMission(v);
                }

                // do nothing else for invalid vessels
                if (!vi.is_valid)
                {
                    continue;
                }

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

                // get resource cache
                Vessel_Resources resources = ResourceCache.Get(v);

                // if loaded
                if (v.loaded)
                {
                    // show belt warnings
                    Radiation.BeltWarnings(v, vi, vd);

                    // update storm data
                    Storm.Update(v, vi, vd, elapsed_s);

                    // show signal warnings
                    Signal.Update(v, vi, vd, elapsed_s);

                    // consume ec for transmission, and transmit science data
                    Science.Update(v, vi, vd, resources, elapsed_s);

                    // apply rules
                    Profile.Execute(v, vi, vd, resources, elapsed_s);

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

                    // call automation scripts
                    vd.computer.Automate(v, vi, resources);

                    // remove from unloaded data container
                    unloaded.Remove(vi.id);
                }
                // if unloaded
                else
                {
                    // get unloaded data, or create an empty one
                    if (!unloaded.TryGetValue(vi.id, out Unloaded_Data ud))
                    {
                        ud = new Unloaded_Data();
                        unloaded.Add(vi.id, ud);
                    }

                    // accumulate time
                    ud.time += elapsed_s;

                    // maintain oldest entry
                    if (ud.time > last_time)
                    {
                        last_time      = ud.time;
                        last_v         = v;
                        last_vi        = vi;
                        last_vd        = vd;
                        last_resources = resources;
                    }
                }
            }

            // if the oldest unloaded vessel was selected
            if (last_v != null)
            {
                // show belt warnings
                Radiation.BeltWarnings(last_v, last_vi, last_vd);

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

                // show signal warnings
                Signal.Update(last_v, last_vi, last_vd, last_time);

                // consume ec for transmission, and transmit science
                Science.Update(last_v, last_vi, last_vd, last_resources, last_time);

                // apply rules
                Profile.Execute(last_v, last_vi, last_vd, last_resources, last_time);

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

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

                // call automation scripts
                last_vd.computer.Automate(last_v, last_vi, last_resources);

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

            // update storm data for one body per-step
            if (storm_bodies.Count > 0)
            {
                storm_bodies.ForEach(k => k.time += elapsed_s);
                Storm_Data sd = storm_bodies[storm_index];
                Storm.Update(sd.body, sd.time);
                sd.time     = 0.0;
                storm_index = (storm_index + 1) % storm_bodies.Count;
            }
        }
        // consume EC for transmission, and transmit science data
        public static void Update(Vessel v, Vessel_Info vi, VesselData vd, Vessel_Resources resources, double elapsed_s)
        {
            // hard-coded transmission buffer size in Mb
            const double buffer_capacity = 8.0;

            // do nothing if science system is disabled
            if (!Features.Science)
            {
                return;
            }

            // avoid corner-case when RnD isn't live during scene changes
            // - this avoid losing science if the buffer reach threshold during a scene change
            if (HighLogic.CurrentGame.Mode != Game.Modes.SANDBOX && ResearchAndDevelopment.Instance == null)
            {
                return;
            }

            // get connection info
            ConnectionInfo conn = vi.connection;

            // consume ec if data is transmitted or relayed
            if (vi.transmitting.Length > 0 || vi.relaying.Length > 0)
            {
                if (Features.KCommNet)
                {
                    // This means that it is only relay, don't send data from yourself
                    // CommNet: When vessel is relay, only antennas that are relay will consume ec
                    if (vi.relaying.Length > 0 && vi.transmitting.Length == 0)
                    {
                        resources.Consume(v, "ElectricCharge", conn.relaycost * elapsed_s);
                    }
                    else
                    {
                        resources.Consume(v, "ElectricCharge", conn.cost * elapsed_s);
                    }
                }
                resources.Consume(v, "ElectricCharge", conn.cost * elapsed_s);
            }

            // get filename of data being downloaded
            string filename = vi.transmitting;

            // if some data is being downloaded
            // - avoid cornercase at scene changes
            if (filename.Length > 0 && vd.drive.files.ContainsKey(filename))
            {
                // get file
                File file = vd.drive.files[filename];

                // determine how much data is transmitted
                double transmitted = Math.Min(file.size, conn.rate * elapsed_s);

                // consume data in the file
                file.size -= transmitted;

                // accumulate in the buffer
                file.buff += transmitted;

                // if buffer is full, or file was transmitted completely
                if (file.size <= double.Epsilon || file.buff > buffer_capacity)
                {
                    // collect the science data
                    Science.Credit(filename, file.buff, true, v.protoVessel);

                    // reset the buffer
                    file.buff = 0.0;
                }

                // if file was transmitted completely
                if (file.size <= double.Epsilon)
                {
                    // remove the file
                    vd.drive.files.Remove(filename);

                    // inform the user
                    Message.Post
                    (
                        Lib.BuildString("<color=cyan><b>DATA RECEIVED</b></color>\nTransmission of <b>", Science.Experiment(filename).name, "</b> completed"),
                        Lib.TextVariant("Our researchers will jump on it right now", "The checksum is correct, data must be valid")
                    );
                }
            }
        }
        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)
                Vessel_Info vi = Cache.VesselInfo(vessel);

                // get resource cache
                Vessel_Resources resources = ResourceCache.Get(vessel);
                Resource_Info    ec        = resources.Info(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    = vi.solar_flux;
                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);
                }

                // 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
                Resource_Recipe recipe = new Resource_Recipe();
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    recipe.Input(input.name, input.rate * Kerbalism.elapsed_s);
                }
                foreach (ModuleResource output in resHandler.outputResources)
                {
                    recipe.Output(output.name, output.rate * Kerbalism.elapsed_s, true);
                }
                resources.Transform(recipe);

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

                // determine input resources conditions
                // - comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    if (resources.Info(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(Lib.BuildString("On <b>", vessel.vesselName, "</b> 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("missing ", missing_res)
                        : !lighting  ? "insufficient lighting"
        : !pressure  ? "insufficient pressure"
        : !radiation ? "excessive radiation"
        : string.Empty;
            }
        }
Exemple #17
0
 static void ProcessGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleGenerator generator, Vessel_Resources resources, double elapsed_s)
 {
     // if active
     if (Lib.Proto.GetBool(m, "generatorIsActive"))
     {
         // create and commit recipe
         Resource_Recipe recipe = new Resource_Recipe();
         foreach (ModuleResource ir in generator.resHandler.inputResources)
         {
             recipe.Input(ir.name, ir.rate * elapsed_s);
         }
         foreach (ModuleResource or in generator.resHandler.outputResources)
         {
             recipe.Output(or.name, or.rate * elapsed_s, true);
         }
         resources.Transform(recipe);
     }
 }
        public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Greenhouse g,
                                            Vessel_Info vi, Vessel_Resources 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
                Resource_Info ec = resources.Info(v, "ElectricCharge");

                // calculate natural and artificial lighting
                double natural    = vi.solar_flux;
                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);
                }

                // 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
                Resource_Recipe recipe = new Resource_Recipe();
                foreach (ModuleResource input in g.resHandler.inputResources)
                {
                    recipe.Input(input.name, input.rate * elapsed_s);
                }
                foreach (ModuleResource output in g.resHandler.outputResources)
                {
                    recipe.Output(output.name, output.rate * elapsed_s, true);
                }
                resources.Transform(recipe);

                // determine environment conditions
                bool lighting  = natural + artificial >= g.light_tolerance;
                bool pressure  = g.pressure_tolerance <= double.Epsilon || vi.pressure >= g.pressure_tolerance;
                bool radiation = g.radiation_tolerance <= double.Epsilon || vi.radiation * (1.0 - vi.shielding) < g.radiation_tolerance;

                // determine inputs conditions
                // note: comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                foreach (ModuleResource input in g.resHandler.inputResources)
                {
                    if (resources.Info(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(Lib.BuildString("On <b>", v.vesselName, "</b> 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("missing ", missing_res)
                        : !lighting  ? "insufficient lighting"
        : !pressure  ? "insufficient pressure"
        : !radiation ? "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);
            }
        }
Exemple #19
0
        // call scripts automatically when conditions are met
        public void Automate(Vessel v, Vessel_Info vi, Vessel_Resources resources)
        {
            // do nothing if automation is disabled
            if (!Features.Automation)
            {
                return;
            }

            // get current states
            Resource_Info ec             = resources.Info(v, "ElectricCharge");
            bool          sunlight       = vi.sunlight > double.Epsilon;
            bool          power_low      = ec.level < 0.2;
            bool          power_high     = ec.level > 0.8;
            bool          radiation_low  = vi.radiation < 0.000005552; //< 0.02 rad/h
            bool          radiation_high = vi.radiation > 0.00001388;  //< 0.05 rad/h
            bool          signal         = vi.connection.linked;

            // get current situation
            bool landed = false;
            bool atmo   = false;
            bool space  = false;

            switch (v.situation)
            {
            case Vessel.Situations.LANDED:
            case Vessel.Situations.SPLASHED:
                landed = true;
                break;

            case Vessel.Situations.FLYING:
                atmo = true;
                break;

            case Vessel.Situations.SUB_ORBITAL:
            case Vessel.Situations.ORBITING:
            case Vessel.Situations.ESCAPING:
                space = true;
                break;
            }

            // compile list of scripts that need to be called
            var to_exec = new List <Script>();

            foreach (var p in scripts)
            {
                ScriptType type   = p.Key;
                Script     script = p.Value;
                if (script.states.Count == 0)
                {
                    continue;                   //< skip empty scripts (may happen during editing)
                }
                switch (type)
                {
                case ScriptType.landed:
                    if (landed && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = landed ? "1" : "0";
                    break;

                case ScriptType.atmo:
                    if (atmo && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = atmo ? "1" : "0";
                    break;

                case ScriptType.space:
                    if (space && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = space ? "1" : "0";
                    break;

                case ScriptType.sunlight:
                    if (sunlight && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = sunlight ? "1" : "0";
                    break;

                case ScriptType.shadow:
                    if (!sunlight && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = !sunlight ? "1" : "0";
                    break;

                case ScriptType.power_high:
                    if (power_high && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = power_high ? "1" : "0";
                    break;

                case ScriptType.power_low:
                    if (power_low && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = power_low ? "1" : "0";
                    break;

                case ScriptType.rad_low:
                    if (radiation_low && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = radiation_low ? "1" : "0";
                    break;

                case ScriptType.rad_high:
                    if (radiation_high && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = radiation_high ? "1" : "0";
                    break;

                case ScriptType.linked:
                    if (signal && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = signal ? "1" : "0";
                    break;

                case ScriptType.unlinked:
                    if (!signal && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = !signal ? "1" : "0";
                    break;
                }
            }

            // if there are scripts to call
            if (to_exec.Count > 0)
            {
                // get list of devices
                // - we avoid creating it when there are no scripts to be executed, making its overall cost trivial
                var devices = Boot(v);

                // execute all scripts
                foreach (Script script in to_exec)
                {
                    script.Execute(devices);
                }

                // show message to the user
                if (DB.Vessel(v).cfg_script)
                {
                    Message.Post(Lib.BuildString("Script called on vessel <b>", v.vesselName, "</b>"));
                }
            }
        }
Exemple #20
0
        public static void Update(Vessel v, Vessel_Info vi, VesselData vd, Vessel_Resources resources, double elapsed_s)
        {
            // get most used resource handlers
            Resource_Info ec = resources.Info(v, "ElectricCharge");

            // store data required to support multiple modules of same type in a part
            var PD = new Dictionary <string, Lib.module_prefab_data>();

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

                // get all module prefabs
                var module_prefabs = part_prefab.FindModulesImplementing <PartModule>();

                // clear module indexes
                PD.Clear();

                // for each module
                foreach (ProtoPartModuleSnapshot m in p.modules)
                {
                    // get module type
                    // if the type is unknown, skip it
                    Module_Type type = ModuleType(m.moduleName);
                    if (type == Module_Type.Unknown)
                    {
                        continue;
                    }

                    // get the module prefab
                    // if the prefab doesn't contain this module, skip it
                    PartModule module_prefab = Lib.ModulePrefab(module_prefabs, m.moduleName, PD);
                    if (!module_prefab)
                    {
                        continue;
                    }

                    // if the module is disabled, skip it
                    // note: this must be done after ModulePrefab is called, so that indexes are right
                    if (!Lib.Proto.GetBool(m, "isEnabled"))
                    {
                        continue;
                    }

                    // process modules
                    // note: this should be a fast switch, possibly compiled to a jump table
                    switch (type)
                    {
                    case Module_Type.Reliability:           Reliability.BackgroundUpdate(v, p, m, module_prefab as Reliability);                        break;

                    case Module_Type.Experiment:            Experiment.BackgroundUpdate(v, m, module_prefab as Experiment, ec, elapsed_s);              break;

                    case Module_Type.Greenhouse:            Greenhouse.BackgroundUpdate(v, m, module_prefab as Greenhouse, vi, resources, elapsed_s);   break;

                    case Module_Type.GravityRing:           GravityRing.BackgroundUpdate(v, p, m, module_prefab as GravityRing, ec, elapsed_s);         break;

                    case Module_Type.Emitter:               Emitter.BackgroundUpdate(v, p, m, module_prefab as Emitter, ec, elapsed_s);                 break;

                    case Module_Type.Harvester:             Harvester.BackgroundUpdate(v, m, module_prefab as Harvester, elapsed_s);                    break;

                    case Module_Type.Laboratory:            Laboratory.BackgroundUpdate(v, p, m, module_prefab as Laboratory, ec, elapsed_s);           break;

                    case Module_Type.Command:               ProcessCommand(v, p, m, module_prefab as ModuleCommand, resources, elapsed_s);              break;

                    case Module_Type.Panel:                 ProcessPanel(v, p, m, module_prefab as ModuleDeployableSolarPanel, vi, ec, elapsed_s);      break;

                    case Module_Type.Generator:             ProcessGenerator(v, p, m, module_prefab as ModuleGenerator, resources, elapsed_s);          break;

                    case Module_Type.Converter:             ProcessConverter(v, p, m, module_prefab as ModuleResourceConverter, resources, elapsed_s);  break;

                    case Module_Type.Drill:                 ProcessHarvester(v, p, m, module_prefab as ModuleResourceHarvester, resources, elapsed_s);  break;

                    case Module_Type.AsteroidDrill:         ProcessAsteroidDrill(v, p, m, module_prefab as ModuleAsteroidDrill, resources, elapsed_s);  break;

                    case Module_Type.StockLab:              ProcessStockLab(v, p, m, module_prefab as ModuleScienceConverter, ec, elapsed_s);           break;

                    case Module_Type.Light:                 ProcessLight(v, p, m, module_prefab as ModuleLight, ec, elapsed_s);                         break;

                    case Module_Type.Scanner:               ProcessScanner(v, p, m, module_prefab, part_prefab, vd, ec, elapsed_s);                     break;

                    case Module_Type.CurvedPanel:           ProcessCurvedPanel(v, p, m, module_prefab, part_prefab, vi, ec, elapsed_s);                 break;

                    case Module_Type.FissionGenerator:      ProcessFissionGenerator(v, p, m, module_prefab, ec, elapsed_s);                             break;

                    case Module_Type.RadioisotopeGenerator: ProcessRadioisotopeGenerator(v, p, m, module_prefab, ec, elapsed_s);                        break;

                    case Module_Type.CryoTank:              ProcessCryoTank(v, p, m, module_prefab, resources, elapsed_s);                              break;

                    case Module_Type.AntennaDeploy:         AntennaDeploy.BackgroundUpdate(v, p, m, vi, ec, elapsed_s);                                 break;
                    }
                }
            }
        }
        static void ProcessCryoTank(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule simple_boiloff, Vessel_Resources resources, double elapsed_s)
        {
            // note: cryotank module already does a post-facto simulation of background boiling, and we could use that for the boiling
            // however, it also does simulate the ec consumption that way, so we have to disable the post-facto simulation

            // As far as I know, Simple_boiloff consumes fuel and EC only if the fuel is on the Fuel List (previously added in Game Load)
            // The test that I did, I was able to enable to "Enable Cooling" only when fuel was into List<BoiloffFuel> fuels

            // get fuel name: FuelName in Simple_Boiloff is always NULL, this Get doesn't make sense, but I will leave here.
            string fuel_name = Lib.ReflectionValue <string>(simple_boiloff, "FuelName");

            // get resource handlers
            Resource_Info ec = resources.Info(v, "ElectricCharge");

            // if fuel_name is null as expected, this would cause error
            if (string.IsNullOrEmpty(fuel_name))
            {
                // Take name from fuel list inside part
                System.Collections.IList fuelList = Lib.ReflectionValue <System.Collections.IList>(simple_boiloff, "fuels");

                foreach (var item in fuelList)
                {
                    fuel_name = (string)item.GetType().GetField("fuelName").GetValue(item);
                    // if fuel_name still null, do anything
                    if (fuel_name == null)
                    {
                        continue;
                    }

                    Resource_Info fuel = resources.Info(v, fuel_name);

                    // if there is some fuel
                    // note: comparing against amount in previous simulation step
                    if (fuel.amount > double.Epsilon)
                    {
                        // Try find resource "fuel_name" into PartResources
                        ProtoPartResourceSnapshot protoPartResource = p.resources.Find(k => k.resourceName == fuel_name);

                        // If part doesn't have the fuel, do anything.
                        if (protoPartResource == null)
                        {
                            continue;
                        }

                        // get capacity in the part
                        double capacity = protoPartResource.maxAmount;

                        // if cooling is enabled and there was enough ec
                        // note: comparing against amount in previous simulation step
                        if (Lib.Proto.GetBool(m, "CoolingEnabled") && ec.amount > double.Epsilon)
                        {
                            // get cooling ec cost per 1000 units of fuel, per-second
                            double cooling_cost = Lib.ReflectionValue <float>(simple_boiloff, "CoolingCost");

                            // consume ec
                            ec.Consume(cooling_cost * capacity * 0.001 * elapsed_s);
                        }
                        // if there wasn't ec, or if cooling is disabled
                        else
                        {
                            // get boiloff rate in proportion to fuel amount, per-second
                            double boiloff_rate = Lib.ReflectionValue <float>(simple_boiloff, "BoiloffRate") * 0.00000277777;

                            // let it boil off
                            fuel.Consume(capacity * (1.0 - Math.Pow(1.0 - boiloff_rate, elapsed_s)));
                        }
                    }

                    // disable post-facto simulation
                    Lib.Proto.Set(m, "LastUpdateTime", v.missionTime);
                }
            }
            else
            {
                Resource_Info fuel = resources.Info(v, fuel_name);

                // if there is some fuel
                // note: comparing against amount in previous simulation step
                if (fuel.amount > double.Epsilon)
                {
                    // Try find resource "fuel_name" into PartResources
                    ProtoPartResourceSnapshot protoPartResource = p.resources.Find(k => k.resourceName == fuel_name);

                    // If part doesn't have the fuel, do anything.
                    if (protoPartResource == null)
                    {
                        return;
                    }

                    // get capacity in the part
                    double capacity = protoPartResource.maxAmount;

                    // if cooling is enabled and there was enough ec
                    // note: comparing against amount in previous simulation step
                    if (Lib.Proto.GetBool(m, "CoolingEnabled") && ec.amount > double.Epsilon)
                    {
                        // get cooling ec cost per 1000 units of fuel, per-second
                        double cooling_cost = Lib.ReflectionValue <float>(simple_boiloff, "CoolingCost");

                        // consume ec
                        ec.Consume(cooling_cost * capacity * 0.001 * elapsed_s);
                    }
                    // if there wasn't ec, or if cooling is disabled
                    else
                    {
                        // get boiloff rate in proportion to fuel amount, per-second
                        double boiloff_rate = Lib.ReflectionValue <float>(simple_boiloff, "BoiloffRate") * 0.00000277777;

                        // let it boil off
                        fuel.Consume(capacity * (1.0 - Math.Pow(1.0 - boiloff_rate, elapsed_s)));
                    }
                }

                // disable post-facto simulation
                Lib.Proto.Set(m, "LastUpdateTime", v.missionTime);
            }
        }
Exemple #22
0
        static void ProcessAsteroidDrill(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleAsteroidDrill asteroid_drill, Vessel_Resources resources, double elapsed_s)
        {
            // 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)

            // 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
                        Resource_Recipe recipe = new Resource_Recipe();
                        recipe.Input("ElectricCharge", asteroid_drill.PowerConsumption * elapsed_s);
                        recipe.Output(res_name, res_amount, true);
                        resources.Transform(recipe);

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

                // undo stock behaviour by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
Exemple #23
0
        static void ProcessHarvester(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceHarvester harvester, Vessel_Resources resources, double elapsed_s)
        {
            // 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)

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // do nothing if full
                // note: comparing against previous amount
                if (resources.Info(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
                        Resource_Recipe recipe = new Resource_Recipe();
                        foreach (var ir in harvester.inputList)
                        {
                            recipe.Input(ir.ResourceName, ir.Ratio * elapsed_s);
                        }
                        recipe.Output(harvester.ResourceName, abundance * harvester.Efficiency * exp_bonus * elapsed_s, true);
                        resources.Transform(recipe);
                    }
                }

                // undo stock behaviour by forcing last_update_time to now
                Lib.Proto.Set(m, "lastUpdateTime", Planetarium.GetUniversalTime());
            }
        }
Exemple #24
0
        static void ProcessConverter(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleResourceConverter converter, Vessel_Resources resources, double elapsed_s)
        {
            // note: ignore stock temperature mechanic of converters
            // note: ignore autoshutdown
            // note: non-mandatory resources 'dynamically scale the ratios', that is exactly what mandatory resources do too (DERP ALERT)
            // note: 'undo' stock behaviour 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)
                {
                    Resource_Info res = resources.Info(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
                    Resource_Recipe recipe = new Resource_Recipe();
                    foreach (var ir in converter.inputList)
                    {
                        recipe.Input(ir.ResourceName, ir.Ratio * exp_bonus * elapsed_s);
                    }
                    foreach (var or in converter.outputList)
                    {
                        recipe.Output(or.ResourceName, or.Ratio * exp_bonus * elapsed_s, or.DumpExcess);
                    }
                    resources.Transform(recipe);
                }

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