Ejemplo n.º 1
0
        static void ProcessRadioisotopeGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule radioisotope_generator, ResourceInfo ec, double elapsed_s)
        {
            // note: doesn't support easy mode

            double power        = Lib.ReflectionValue <float>(radioisotope_generator, "BasePower");
            double half_life    = Lib.ReflectionValue <float>(radioisotope_generator, "HalfLife");
            double mission_time = v.missionTime / (3600.0 * Lib.HoursInDay * Lib.DaysInYear);
            double remaining    = Math.Pow(2.0, (-mission_time) / half_life);

            ec.Produce(power * remaining * elapsed_s, ResourceBroker.RTG);
        }
Ejemplo n.º 2
0
        static void ProcessCryoTank(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule cryotank, VesselResources resources, ResourceInfo ec, double elapsed_s)
        {
            // Note. Currently background simulation of Cryotanks has an irregularity in that boiloff of a fuel type in a tank removes resources from all tanks
            // but at least some simulation is better than none ;)

            // get list of fuels, do nothing if no fuels
            IList fuels = Lib.ReflectionValue <IList>(cryotank, "fuels");

            if (fuels == null)
            {
                return;
            }

            // is cooling available, note: comparing against amount in previous simulation step
            bool available = (Lib.Proto.GetBool(m, "CoolingEnabled") && ec.Amount > double.Epsilon);

            // get cooling cost
            double cooling_cost = Lib.ReflectionValue <float>(cryotank, "CoolingCost");

            string fuel_name    = "";
            double amount       = 0.0;
            double total_cost   = 0.0;
            double boiloff_rate = 0.0;

            foreach (var item in fuels)
            {
                fuel_name = Lib.ReflectionValue <string>(item, "fuelName");
                // if fuel_name is null, don't do anything
                if (fuel_name == null)
                {
                    continue;
                }

                //get fuel resource
                ResourceInfo fuel = resources.GetResource(v, fuel_name);

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

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

                    // get amount in the part
                    amount = proto_fuel.amount;

                    // if cooling is enabled and there is enough EC
                    if (available)
                    {
                        // calculate ec consumption
                        total_cost += cooling_cost * amount * 0.001;
                    }
                    // if cooling is disabled or there wasn't any EC
                    else
                    {
                        // get boiloff rate per-second
                        boiloff_rate = Lib.ReflectionValue <float>(item, "boiloffRate") / 360000.0f;

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

            // apply EC consumption
            ec.Consume(total_cost * elapsed_s, ResourceBroker.Cryotank);
        }
Ejemplo n.º 3
0
 static void ProcessLight(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleLight light, ResourceInfo ec, double elapsed_s)
 {
     if (light.useResources && Lib.Proto.GetBool(m, "isOn"))
     {
         ec.Consume(light.resourceAmount * elapsed_s, ResourceBroker.Light);
     }
 }
Ejemplo n.º 4
0
        /*
         * static void ProcessScanner(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule scanner, Part part_prefab, VesselData vd, ResourceInfo ec, double elapsed_s)
         * {
         *      // get ec consumption rate
         *      double power = SCANsat.EcConsumption(scanner);
         *
         *      // if the scanner doesn't require power to operate, we aren't interested in simulating it
         *      if (power <= double.Epsilon) return;
         *
         *      // get scanner state
         *      bool is_scanning = Lib.Proto.GetBool(m, "scanning");
         *
         *      // if its scanning
         *      if (is_scanning)
         *      {
         *              // consume ec
         *              ec.Consume(power * elapsed_s, "scanner");
         *
         *              // if there isn't ec
         *              // - comparing against amount in previous simulation step
         *              if (ec.amount <= double.Epsilon)
         *              {
         *                      // unregister scanner
         *                      SCANsat.StopScanner(v, m, part_prefab);
         *                      is_scanning = false;
         *
         *                      // remember disabled scanner
         *                      vd.scansat_id.Add(p.flightID);
         *
         *                      // give the user some feedback
         *                      if (vd.cfg_ec) Message.Post(Lib.BuildString("SCANsat sensor was disabled on <b>", v.vesselName, "</b>"));
         *              }
         *      }
         *      // if it was disabled in background
         *      else if (vd.scansat_id.Contains(p.flightID))
         *      {
         *              // if there is enough ec
         *              // note: comparing against amount in previous simulation step
         *              // re-enable at 25% EC
         *              if (ec.level > 0.25)
         *              {
         *                      // re-enable the scanner
         *                      SCANsat.ResumeScanner(v, m, part_prefab);
         *                      is_scanning = true;
         *
         *                      // give the user some feedback
         *                      if (vd.cfg_ec) Message.Post(Lib.BuildString("SCANsat sensor resumed operations on <b>", v.vesselName, "</b>"));
         *              }
         *      }
         *
         *      // forget active scanners
         *      if (is_scanning) vd.scansat_id.Remove(p.flightID);
         * }
         */

        static void ProcessFissionGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule fission_generator, ResourceInfo ec, double elapsed_s)
        {
            // note: ignore heat

            double power     = Lib.ReflectionValue <float>(fission_generator, "PowerGeneration");
            var    reactor   = p.modules.Find(k => k.moduleName == "FissionReactor");
            double tweakable = reactor == null ? 1.0 : Lib.ConfigValue(reactor.moduleValues, "CurrentPowerPercent", 100.0) * 0.01;

            ec.Produce(power * tweakable * elapsed_s, ResourceBroker.FissionReactor);
        }
Ejemplo n.º 5
0
        static void ProcessFNGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule fission_generator, ResourceInfo ec, double elapsed_s)
        {
            string maxPowerStr = Lib.Proto.GetString(m, "MaxPowerStr");
            double maxPower    = 0;

            if (maxPowerStr.Contains("GW"))
            {
                maxPower = double.Parse(maxPowerStr.Replace(" GW", "")) * 1000000;
            }
            else if (maxPowerStr.Contains("MW"))
            {
                maxPower = double.Parse(maxPowerStr.Replace(" MW", "")) * 1000;
            }
            else
            {
                maxPower = double.Parse(maxPowerStr.Replace(" KW", ""));
            }

            ec.Produce(maxPower * elapsed_s, ResourceBroker.KSPIEGenerator);
        }
Ejemplo n.º 6
0
        static void ProcessStockLab(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleScienceConverter lab, ResourceInfo ec, double elapsed_s)
        {
            // note: we are only simulating the EC consumption
            // note: there is no easy way to 'stop' the lab when there isn't enough EC

            // if active
            if (Lib.Proto.GetBool(m, "IsActivated"))
            {
                // consume ec
                ec.Consume(lab.powerRequirement * elapsed_s, ResourceBroker.ScienceLab);
            }
        }
Ejemplo n.º 7
0
        // call scripts automatically when conditions are met
        public void Automate(Vessel v, VesselData vd, VesselResources resources)
        {
            // do nothing if automation is disabled
            if (!Features.Automation)
            {
                return;
            }

            // get current states
            ResourceInfo ec             = resources.GetResource(v, "ElectricCharge");
            bool         sunlight       = !vd.EnvInFullShadow;
            bool         power_low      = ec.Level < 0.2;
            bool         power_high     = ec.Level > 0.8;
            bool         radiation_low  = vd.EnvRadiation < 0.000005552;    //< 0.02 rad/h
            bool         radiation_high = vd.EnvRadiation > 0.00001388;     //< 0.05 rad/h
            bool         signal         = vd.Connection.linked;
            bool         drive_full     = vd.DrivesFreeSpace < double.MaxValue && (vd.DrivesFreeSpace / vd.DrivesCapacity < 0.15);
            bool         drive_empty    = vd.DrivesFreeSpace >= double.MaxValue || (vd.DrivesFreeSpace / vd.DrivesCapacity > 0.9);

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

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

                case ScriptType.drive_empty:
                    if (drive_empty && script.prev == "0")
                    {
                        to_exec.Add(script);
                    }
                    script.prev = drive_empty ? "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
                List <Device> devices = GetModuleDevices(v);

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

                // show message to the user
                if (v.KerbalismData().cfg_script)
                {
                    Message.Post(Lib.BuildString("Script called on vessel <b>", v.vesselName, "</b>"));
                }
            }
        }
Ejemplo n.º 8
0
        public static void Update(Vessel v, VesselData vd, VesselResources resources, double elapsed_s)
        {
            if (!Lib.IsVessel(v))
            {
                return;
            }

            // get most used resource handlers
            ResourceInfo ec = resources.GetResource(v, "ElectricCharge");

            List <ResourceInfo>         allResources       = resources.GetAllResources(v);
            Dictionary <string, double> availableResources = new Dictionary <string, double>();

            foreach (var ri in allResources)
            {
                availableResources[ri.ResourceName] = ri.Amount;
            }
            List <KeyValuePair <string, double> > resourceChangeRequests = new List <KeyValuePair <string, double> >();

            foreach (var e in Background_PMs(v))
            {
                switch (e.type)
                {
                case Module_type.Reliability: Reliability.BackgroundUpdate(v, e.p, e.m, e.module_prefab as Reliability, elapsed_s); break;

                case Module_type.Experiment: (e.module_prefab as Experiment).BackgroundUpdate(v, vd, e.m, ec, resources, elapsed_s); break;                         // experiments use the prefab as a singleton instead of a static method

                case Module_type.Greenhouse: Greenhouse.BackgroundUpdate(v, e.m, e.module_prefab as Greenhouse, vd, resources, elapsed_s); break;

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

                case Module_type.Harvester: Harvester.BackgroundUpdate(v, e.m, e.module_prefab as Harvester, elapsed_s); break;                         // Kerbalism ground and air harvester module

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

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

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

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

                case Module_type.Drill: ProcessDrill(v, e.p, e.m, e.module_prefab as ModuleResourceHarvester, resources, elapsed_s); break;                         // Stock ground harvester module

                case Module_type.AsteroidDrill: ProcessAsteroidDrill(v, e.p, e.m, e.module_prefab as ModuleAsteroidDrill, resources, elapsed_s); break;             // Stock asteroid harvester module

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

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

                case Module_type.Scanner: KerbalismScansat.BackgroundUpdate(v, e.p, e.m, e.module_prefab as KerbalismScansat, e.part_prefab, vd, ec, elapsed_s); break;

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

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

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

                case Module_type.FNGenerator: ProcessFNGenerator(v, e.p, e.m, e.module_prefab, ec, elapsed_s); break;

                case Module_type.SolarPanelFixer: SolarPanelFixer.BackgroundUpdate(v, e.m, e.module_prefab as SolarPanelFixer, vd, ec, elapsed_s); break;

                case Module_type.APIModule: ProcessApiModule(v, e.p, e.m, e.part_prefab, e.module_prefab, resources, availableResources, resourceChangeRequests, elapsed_s); break;
                }
            }
        }
Ejemplo n.º 9
0
        // trigger a random breakdown event
        public static void Breakdown(Vessel v, ProtoCrewMember c)
        {
            // constants
            const double res_penalty = 0.1;                    // proportion of food lost on 'depressed' and 'wrong_valve'

            // get a supply resource at random
            ResourceInfo res = null;

            if (Profile.supplies.Count > 0)
            {
                Supply supply = Profile.supplies[Lib.RandomInt(Profile.supplies.Count)];
                res = ResourceCache.GetResource(v, supply.resource);
            }

            // compile list of events with condition satisfied
            List <KerbalBreakdown> events = new List <KerbalBreakdown>
            {
                KerbalBreakdown.mumbling                 //< do nothing, here so there is always something that can happen
            };

            if (Lib.HasData(v))
            {
                events.Add(KerbalBreakdown.fat_finger);
            }
            if (Reliability.CanMalfunction(v))
            {
                events.Add(KerbalBreakdown.rage);
            }
            if (res != null && res.Amount > double.Epsilon)
            {
                events.Add(KerbalBreakdown.wrong_valve);
            }

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

            // generate message
            string text    = "";
            string subtext = "";

            switch (breakdown)
            {
            case KerbalBreakdown.mumbling:
                text    = Local.Kerbalmumbling;                     //"$ON_VESSEL$KERBAL has been in space for too long"
                subtext = Local.Kerbalmumbling_subtext;             //"Mumbling incoherently"
                break;

            case KerbalBreakdown.fat_finger:
                text    = Local.Kerbalfatfinger_subtext;                     //"$ON_VESSEL$KERBAL is pressing buttons at random on the control panel"
                subtext = Local.Kerbalfatfinger_subtext;                     //"Science data has been lost"
                break;

            case KerbalBreakdown.rage:
                text    = Local.Kerbalrage;                     //"$ON_VESSEL$KERBAL is possessed by a blind rage"
                subtext = Local.Kerbalrage_subtext;             //"A component has been damaged"
                break;

            case KerbalBreakdown.wrong_valve:
                text    = Local.Kerbalwrongvalve;                                  //"$ON_VESSEL$KERBAL opened the wrong valve"
                subtext = res.ResourceName + " " + Local.Kerbalwrongvalve_subtext; //has been lost"
                break;
            }

            // post message first so this one is shown before malfunction message
            Message.Post(Severity.breakdown, Lib.ExpandMsg(text, v, c), subtext);

            // trigger the event
            switch (breakdown)
            {
            case KerbalBreakdown.mumbling:
                break;                         // do nothing

            case KerbalBreakdown.fat_finger:
                Lib.RemoveData(v);
                break;

            case KerbalBreakdown.rage:
                Reliability.CauseMalfunction(v);
                break;

            case KerbalBreakdown.wrong_valve:
                res.Consume(res.Amount * res_penalty, ResourceBroker.Generic);
                break;
            }

            // remove reputation
            if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
            {
                Reputation.Instance.AddReputation(-Settings.KerbalBreakdownReputationPenalty, TransactionReasons.Any);
            }
        }
Ejemplo n.º 10
0
        void FixedUpdate()
        {
            // remove control locks in any case
            Misc.ClearLocks();

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

            // convert elapsed time to double only once
            double fixedDeltaTime = TimeWarp.fixedDeltaTime;

            // and detect warp blending
            if (Math.Abs(fixedDeltaTime - elapsed_s) < 0.001)
            {
                warp_blending = 0;
            }
            else
            {
                ++warp_blending;
            }

            // update elapsed time
            elapsed_s = fixedDeltaTime;

            // store info for oldest unloaded vessel
            double          last_time      = 0.0;
            Guid            last_id        = Guid.Empty;
            Vessel          last_v         = null;
            VesselData      last_vd        = null;
            VesselResources last_resources = null;

            foreach (VesselData vd in DB.VesselDatas)
            {
                vd.EarlyUpdate();
            }

            // for each vessel
            foreach (Vessel v in FlightGlobals.Vessels)
            {
                // get vessel data
                VesselData vd = v.KerbalismData();

                // update the vessel data validity
                vd.Update(v);

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

                // 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 && vd.is_vessel)
                {
                    // manage rescue mission mechanics
                    Misc.ManageRescueMission(v);
                }

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

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

                // if loaded
                if (v.loaded)
                {
                    //UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.VesselDataEval");
                    // update the vessel info
                    vd.Evaluate(false, elapsed_s);
                    //UnityEngine.Profiling.Profiler.EndSample();

                    // get most used resource
                    ResourceInfo ec = resources.GetResource(v, "ElectricCharge");

                    UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Radiation");
                    // show belt warnings
                    Radiation.BeltWarnings(v, vd);

                    // update storm data
                    Storm.Update(v, vd, elapsed_s);
                    UnityEngine.Profiling.Profiler.EndSample();

                    UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Comms");
                    Communications.Update(v, vd, ec, elapsed_s);
                    UnityEngine.Profiling.Profiler.EndSample();

                    // Habitat equalization
                    ResourceBalance.Equalizer(v);

                    UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Science");
                    // transmit science data
                    Science.Update(v, vd, ec, elapsed_s);
                    UnityEngine.Profiling.Profiler.EndSample();

                    UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Profile");
                    // apply rules
                    Profile.Execute(v, vd, resources, elapsed_s);
                    UnityEngine.Profiling.Profiler.EndSample();

                    UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Profile");
                    // part module resource updates
                    vd.ResourceUpdate(resources, elapsed_s);
                    UnityEngine.Profiling.Profiler.EndSample();

                    UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Loaded.Resource");
                    // apply deferred requests
                    resources.Sync(v, vd, elapsed_s);
                    UnityEngine.Profiling.Profiler.EndSample();

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

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

                    // accumulate time
                    ud.time += elapsed_s;

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

            // at most one vessel gets background processing per physics tick :
            // if there is a vessel that is not the currently loaded vessel, then
            // we will update the vessel whose most recent background update is the oldest
            if (last_v != null)
            {
                //UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.VesselDataEval");
                // update the vessel info (high timewarp speeds reevaluation)
                last_vd.Evaluate(false, last_time);
                //UnityEngine.Profiling.Profiler.EndSample();

                // get most used resource
                ResourceInfo last_ec = last_resources.GetResource(last_v, "ElectricCharge");

                UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Radiation");
                // show belt warnings
                Radiation.BeltWarnings(last_v, last_vd);

                // update storm data
                Storm.Update(last_v, last_vd, last_time);
                UnityEngine.Profiling.Profiler.EndSample();

                UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Comms");
                Communications.Update(last_v, last_vd, last_ec, last_time);
                UnityEngine.Profiling.Profiler.EndSample();

                UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Profile");
                // apply rules
                Profile.Execute(last_v, last_vd, last_resources, last_time);
                UnityEngine.Profiling.Profiler.EndSample();

                UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Background");
                // simulate modules in background
                Background.Update(last_v, last_vd, last_resources, last_time);
                UnityEngine.Profiling.Profiler.EndSample();

                UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Science");
                // transmit science	data
                Science.Update(last_v, last_vd, last_ec, last_time);
                UnityEngine.Profiling.Profiler.EndSample();

                UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.FixedUpdate.Unloaded.Resource");
                // apply deferred requests
                last_resources.Sync(last_v, last_vd, last_time);
                UnityEngine.Profiling.Profiler.EndSample();

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

                // remove from unloaded data container
                unloaded.Remove(last_vd.VesselId);
            }

            // 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;
            }
        }
Ejemplo n.º 11
0
        public static void Update(Vessel v, VesselData vd, ResourceInfo ec, double elapsed_s)
        {
            if (!Lib.IsVessel(v))
            {
                return;
            }

            // EC consumption is handled in Science update

            Cache.WarpCache(v).dataCapacity = vd.deviceTransmit ? vd.Connection.rate * elapsed_s : 0.0;

            // do nothing if network is not ready
            if (!NetworkInitialized)
            {
                return;
            }

            // maintain and send messages
            // - do not send messages during/after solar storms
            // - do not send messages for EVA kerbals
            if (!v.isEVA && v.situation != Vessel.Situations.PRELAUNCH)
            {
                if (!vd.msg_signal && !vd.Connection.linked)
                {
                    vd.msg_signal = true;
                    if (vd.cfg_signal)
                    {
                        string subtext = Localizer.Format("#KERBALISM_UI_transmissiondisabled");

                        switch (vd.Connection.status)
                        {
                        case LinkStatus.plasma:
                            subtext = Localizer.Format("#KERBALISM_UI_Plasmablackout");
                            break;

                        case LinkStatus.storm:
                            subtext = Localizer.Format("#KERBALISM_UI_Stormblackout");
                            break;

                        default:
                            if (vd.CrewCount == 0)
                            {
                                switch (Settings.UnlinkedControl)
                                {
                                case UnlinkedCtrl.none:
                                    subtext = Localizer.Format("#KERBALISM_UI_noctrl");
                                    break;

                                case UnlinkedCtrl.limited:
                                    subtext = Localizer.Format("#KERBALISM_UI_limitedcontrol");
                                    break;
                                }
                            }
                            break;
                        }

                        Message.Post(Severity.warning, Lib.BuildString(Localizer.Format("#KERBALISM_UI_signallost"), " <b>", v.vesselName, "</b>"), subtext);
                    }
                }
                else if (vd.msg_signal && vd.Connection.linked)
                {
                    vd.msg_signal = false;
                    if (vd.cfg_signal)
                    {
                        Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> ", Localizer.Format("#KERBALISM_UI_signalback")),
                                     vd.Connection.status == LinkStatus.direct_link ? Localizer.Format("#KERBALISM_UI_directlink") :
                                     Lib.BuildString(Localizer.Format("#KERBALISM_UI_relayby"), " <b>", vd.Connection.target_name, "</b>"));
                    }
                }
            }
        }
Ejemplo n.º 12
0
 public static void BackgroundUpdate(Vessel vessel, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, GravityRing ring, ResourceInfo ec, double elapsed_s)
 {
     // if the module is either non-deployable or deployed
     if (ring.deploy.Length == 0 || Lib.Proto.GetBool(m, "deployed"))
     {
         // consume ec
         ec.Consume(ring.ec_rate * elapsed_s, ResourceBroker.GravityRing);
     }
 }
Ejemplo n.º 13
0
        // This Method has a lot of "For\Foreach" because it was design for multi resources
        // Method don't count disabled habitats
        public static void Equalizer(Vessel v)
        {
            // get resource level in habitats
            double[] res_level = new double[resourceName.Length];                               // Don't count Manned or Depressiong habitats

            // Total resource in parts not disabled
            double[] totalAmount = new double[resourceName.Length];
            double[] maxAmount   = new double[resourceName.Length];

            // Total resource in Enabled parts (No crew)
            double[] totalE = new double[resourceName.Length];
            double[] maxE   = new double[resourceName.Length];

            // Total resource in Manned parts (Priority!)
            double[] totalP = new double[resourceName.Length];
            double[] maxP   = new double[resourceName.Length];

            // Total resource in Depressurizing
            double[] totalD = new double[resourceName.Length];
            double[] maxD   = new double[resourceName.Length];

            // amount to equalize speed
            double[] amount = new double[resourceName.Length];

            // Can be positive or negative, controlling the resource flow
            double flowController;

            bool[] mannedisPriority = new bool[resourceName.Length];                            // The resource is priority
            bool   equalize         = false;                                                    // Has any resource that needs to be equalized

            // intial value
            for (int i = 0; i < resourceName.Length; i++)
            {
                totalAmount[i] = new ResourceInfo(v, resourceName[i]).Rate;                        // Get generate rate for each resource
                maxAmount[i]   = 0;

                totalE[i] = 0;
                maxE[i]   = 0;

                totalP[i] = 0;
                maxP[i]   = 0;

                totalD[i] = 0;
                maxD[i]   = 0;

                mannedisPriority[i] = false;
            }

            foreach (Habitat partHabitat in v.FindPartModulesImplementing <Habitat>())
            {
                // Skip disabled habitats
                if (partHabitat.state != Habitat.State.disabled)
                {
                    // Has flag to be Equalized?
                    equalize |= partHabitat.needEqualize;

                    PartResource[] resources = new PartResource[resourceName.Length];
                    for (int i = 0; i < resourceName.Length; i++)
                    {
                        if (partHabitat.part.Resources.Contains(resourceName[i]))
                        {
                            PartResource t = partHabitat.part.Resources[resourceName[i]];

                            // Manned Amounts
                            if (Lib.IsCrewed(partHabitat.part))
                            {
                                totalP[i] += t.amount;
                                maxP[i]   += t.maxAmount;
                            }
                            // Amount for Depressurizing
                            else if (partHabitat.state == Habitat.State.depressurizing)
                            {
                                totalD[i] += t.amount;
                                maxD[i]   += t.maxAmount;
                            }
                            else
                            {
                                totalE[i] += t.amount;
                                maxE[i]   += t.maxAmount;
                            }
                            totalAmount[i] += t.amount;
                            maxAmount[i]   += t.maxAmount;
                        }
                    }
                }
            }

            if (!equalize)
            {
                return;
            }

            for (int i = 0; i < resourceName.Length; i++)
            {
                // resource level for Enabled habitats no Manned
                res_level[i] = totalE[i] / (maxAmount[i] - maxP[i]);

                // Manned is priority?
                // If resource amount is less then maxAmount in manned habitat and it's flagged to equalize, define as priority
                // Using Atmosphere, N2, O2 as Priority trigger (we don't want to use CO2 as a trigger)
                if (resourceName[i] != "WasteAtmosphere" && equalize)
                {
                    mannedisPriority[i] = maxP[i] - totalP[i] > 0;
                }

                // determine generic equalization speed	per resource
                if (mannedisPriority[i])
                {
                    amount[i] = maxAmount[i] * equalize_speed * Kerbalism.elapsed_s;
                }
                else
                {
                    amount[i] = (maxE[i] + maxD[i]) * equalize_speed * Kerbalism.elapsed_s;
                }
            }

            if (equalize)
            {
                foreach (Habitat partHabitat in v.FindPartModulesImplementing <Habitat>())
                {
                    bool stillNeed = false;
                    if (partHabitat.state != Habitat.State.disabled)
                    {
                        for (int i = 0; i < resourceName.Length; i++)
                        {
                            if (partHabitat.part.Resources.Contains(resourceName[i]))
                            {
                                PartResource t = partHabitat.part.Resources[resourceName[i]];
                                flowController = 0;

                                // Conditions in order
                                // If perctToMax = 0 (means Habitat will have 0% of amount:
                                //	1 case: modules still needs to be equalized
                                //	2 case: has depressurizing habitat
                                //	3 case: dropping everything into the priority habitats

                                if ((Math.Abs(res_level[i] - (t.amount / t.maxAmount)) > precision && !Lib.IsCrewed(partHabitat.part)) ||
                                    ((partHabitat.state == Habitat.State.depressurizing ||
                                      mannedisPriority[i]) && t.amount > double.Epsilon))
                                {
                                    double perctToAll;                                                  // Percent of resource for this habitat related
                                    double perctRest;                                                   // Percent to fill priority

                                    perctToAll = t.amount / maxAmount[i];

                                    double perctToType;
                                    double perctToMaxType;

                                    // Percts per Types
                                    if (Lib.IsCrewed(partHabitat.part))
                                    {
                                        perctToType    = t.amount / totalP[i];
                                        perctToMaxType = t.maxAmount / maxP[i];
                                    }
                                    else if (partHabitat.state == Habitat.State.depressurizing)
                                    {
                                        perctToType    = t.amount / totalD[i];
                                        perctToMaxType = t.maxAmount / maxD[i];
                                    }
                                    else
                                    {
                                        perctToType    = t.amount / totalE[i];
                                        perctToMaxType = t.maxAmount / maxE[i];
                                    }

                                    // Perct from the left resource
                                    if (totalAmount[i] - maxP[i] <= 0 || partHabitat.state == Habitat.State.depressurizing)
                                    {
                                        perctRest = 0;
                                    }
                                    else
                                    {
                                        perctRest = (((totalAmount[i] - maxP[i]) * perctToMaxType) - t.amount) / totalE[i];
                                    }

                                    // perctToMax < perctToAll ? habitat will send resource : otherwise will receive, flowController == 0 means no flow
                                    if ((partHabitat.state == Habitat.State.depressurizing || totalAmount[i] - maxP[i] <= 0) && !Lib.IsCrewed(partHabitat.part))
                                    {
                                        flowController = 0 - perctToType;
                                    }
                                    else if (mannedisPriority[i] && !Lib.IsCrewed(partHabitat.part))
                                    {
                                        flowController = Math.Min(perctToMaxType - perctToAll, (t.maxAmount - t.amount) / totalAmount[i]);
                                    }
                                    else
                                    {
                                        flowController = perctRest;
                                    }

                                    // clamp amount to what's available in the hab and what can fit in the part
                                    double amountAffected;
                                    if (partHabitat.state == Habitat.State.depressurizing)
                                    {
                                        amountAffected = flowController * totalD[i];
                                    }
                                    else if (!mannedisPriority[i] || !Lib.IsCrewed(partHabitat.part))
                                    {
                                        amountAffected = flowController * totalE[i];
                                    }
                                    else
                                    {
                                        amountAffected = flowController * totalP[i];
                                    }

                                    amountAffected *= equalize_speed;

                                    amountAffected = Math.Sign(amountAffected) >= 0 ? Math.Max(Math.Sign(amountAffected) * precision, amountAffected) : Math.Min(Math.Sign(amountAffected) * precision, amountAffected);

                                    double va = amountAffected <0.0
                                                                                ? Math.Abs(amountAffected)> t.amount                 // If negative, habitat can't send more than it has
                                                                                ? t.amount * (-1)
                                                                                : amountAffected
                                                : Math.Min(amountAffected, t.maxAmount - t.amount);                                  // if positive, habitat can't receive more than max

                                    va = Double.IsNaN(va) ? 0.0 : va;

                                    // consume relative percent of this part
                                    t.amount += va;

                                    if (va < double.Epsilon)
                                    {
                                        stillNeed = false;
                                    }
                                    else
                                    {
                                        stillNeed = true;
                                    }
                                }
                            }
                        }
                    }

                    partHabitat.needEqualize = stillNeed;
                }
            }
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Execute the recipe and record deferred consumption/production for inputs/ouputs.
        /// This need to be called multiple times until left &lt;= 0.0 for complete execution of the recipe.
        /// return true if recipe execution is completed, false otherwise
        /// </summary>
        private bool ExecuteRecipeStep(Vessel v, VesselResources 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];
                    ResourceInfo res = resources.GetResource(v, e.name);

                    // handle combined inputs
                    if (e.combined != null)
                    {
                        // is combined resource the primary
                        if (e.combined != "")
                        {
                            Entry        sec_e     = inputs.Find(x => x.name.Contains(e.combined));
                            ResourceInfo sec       = resources.GetResource(v, sec_e.name);
                            double       pri_worst = Lib.Clamp((res.Amount + res.Deferred) * e.inv_quantity, 0.0, worst_input);
                            if (pri_worst > 0.0)
                            {
                                worst_input = pri_worst;
                            }
                            else
                            {
                                worst_input = Lib.Clamp((sec.Amount + sec.Deferred) * sec_e.inv_quantity, 0.0, worst_input);
                            }
                        }
                    }
                    else
                    {
                        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
                    {
                        ResourceInfo res = resources.GetResource(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];
                ResourceInfo res = resources.GetResource(v, e.name);
                // handle combined inputs
                if (e.combined != null)
                {
                    // is combined resource the primary
                    if (e.combined != "")
                    {
                        Entry        sec_e = inputs.Find(x => x.name.Contains(e.combined));
                        ResourceInfo sec   = resources.GetResource(v, sec_e.name);
                        double       need  = (e.quantity * worst_io) + (sec_e.quantity * worst_io);
                        // do we have enough primary to satisfy needs, if so don't consume secondary
                        if (res.Amount + res.Deferred >= need)
                        {
                            resources.Consume(v, e.name, need, name);
                        }
                        // consume primary if any available and secondary
                        else
                        {
                            need -= res.Amount + res.Deferred;
                            res.Consume(res.Amount + res.Deferred, name);
                            sec.Consume(need, name);
                        }
                    }
                }
                else
                {
                    res.Consume(e.quantity * worst_io, name);
                }
            }

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

            // produce cures
            for (int i = 0; i < cures.Count; ++i)
            {
                Entry           entry       = cures[i];
                List <RuleData> curingRules = new List <RuleData>();
                foreach (ProtoCrewMember crew in v.GetVesselCrew())
                {
                    KerbalData kd = DB.Kerbal(crew.name);
                    if (kd.sickbay.IndexOf(entry.combined + ",", StringComparison.Ordinal) >= 0)
                    {
                        curingRules.Add(kd.Rule(entry.name));
                    }
                }

                foreach (RuleData rd in curingRules)
                {
                    rd.problem -= entry.quantity * worst_io / curingRules.Count;
                    rd.problem  = Math.Max(rd.problem, 0);
                }
            }

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

            // the recipe was executed, at least partially
            return(worst_io > double.Epsilon);
        }
Ejemplo n.º 15
0
        static void Render_supplies(Panel p, Vessel v, VesselData vd, VesselResources resources)
        {
            int supplies = 0;

            // for each supply
            foreach (Supply supply in Profile.supplies)
            {
                // get resource info
                ResourceInfo res = resources.GetResource(v, supply.resource);

                // only show estimate if the resource is present
                if (res.Capacity <= 1e-10)
                {
                    continue;
                }

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

                // determine label
                var    resource = PartResourceLibrary.Instance.resourceDefinitions[supply.resource];
                string label    = Lib.SpacesOnCaps(resource.displayName).ToLower();

                StringBuilder sb = new StringBuilder();

                sb.Append("<align=left />");
                if (res.AverageRate != 0.0)
                {
                    sb.Append(Lib.Color(res.AverageRate > 0.0,
                                        Lib.BuildString("+", Lib.HumanReadableRate(Math.Abs(res.AverageRate))), Lib.Kolor.PosRate,
                                        Lib.BuildString("-", Lib.HumanReadableRate(Math.Abs(res.AverageRate))), Lib.Kolor.NegRate,
                                        true));
                }
                else
                {
                    sb.Append("<b>no change</b>");
                }

                if (res.AverageRate < 0.0 && res.Level < 0.0001)
                {
                    sb.Append(" <i>(empty)</i>");
                }
                else if (res.AverageRate > 0.0 && res.Level > 0.9999)
                {
                    sb.Append(" <i>(full)</i>");
                }
                else
                {
                    sb.Append("   ");                  // spaces to prevent alignement issues
                }
                sb.Append("\t");
                sb.Append(res.Amount.ToString("F1"));
                sb.Append("/");
                sb.Append(res.Capacity.ToString("F1"));
                sb.Append(" (");
                sb.Append(res.Level.ToString("P0"));
                sb.Append(")");

                List <SupplyData.ResourceBroker> brokers = vd.Supply(supply.resource).ResourceBrokers;
                if (brokers.Count > 0)
                {
                    sb.Append("\n<b>------------    \t------------</b>");
                    foreach (SupplyData.ResourceBroker rb in brokers)
                    {
                        sb.Append("\n");
                        sb.Append(Lib.Color(rb.rate > 0.0,
                                            Lib.BuildString("+", Lib.HumanReadableRate(Math.Abs(rb.rate)), "   "), Lib.Kolor.PosRate,             // spaces to mitigate alignement issues
                                            Lib.BuildString("-", Lib.HumanReadableRate(Math.Abs(rb.rate)), "   "), Lib.Kolor.NegRate,             // spaces to mitigate alignement issues
                                            true));
                        sb.Append("\t");
                        sb.Append(rb.name);
                    }
                }

                string rate_tooltip = sb.ToString();

                // finally, render resource supply
                p.AddContent(label, Lib.HumanReadableDuration(res.DepletionTime()), rate_tooltip);
                ++supplies;
            }
        }