Example #1
0
        public KeyValuePair <bool, double> modReturn;   // Return from ECDevice

        public override void OnStart(StartState state)
        {
            // don't break tutorial scenarios & do something only in Flight scenario
            if (Lib.DisableScenario(this) || !Lib.IsFlight())
            {
                return;
            }

            Lib.Debug("Executing OnStart");
            // cache list of modules
            module = part.FindModulesImplementing <PartModule>().FindLast(k => k.moduleName == type);

            // get energy from cache
            resources = ResourceCache.Info(vessel, "ElectricCharge");
            hasEnergy = resources.amount > double.Epsilon;

            // Force the update to run at least once
            lastBrokenState       = !broken;
            hasEnergyChanged      = !hasEnergy;
            hasFixedEnergyChanged = !hasEnergy;

#if DEBUG
            // setup UI
            Fields["actualCost"].guiActive = true;
            Fields["broken"].guiActive     = true;
#endif
        }
        void Indicator_EC(Panel p, Vessel v, Vessel_Info vi)
        {
            Resource_Info ec            = ResourceCache.Info(v, "ElectricCharge");
            Supply        supply        = Profile.supplies.Find(k => k.resource == "ElectricCharge");
            double        low_threshold = supply != null ? supply.low_threshold : 0.15;
            double        depletion     = ec.Depletion(vi.crew_count);

            string tooltip = Lib.BuildString
                             (
                "<align=left /><b>name\tlevel\tduration</b>\n",
                ec.level <= 0.005 ? "<color=#ff0000>" : ec.level <= low_threshold ? "<color=#ffff00>" : "<color=#cccccc>",
                "EC\t",
                Lib.HumanReadablePerc(ec.level), "\t",
                depletion <= double.Epsilon ? "depleted" : Lib.HumanReadableDuration(depletion),
                "</color>"
                             );

            Texture image = ec.level <= 0.005
        ? Icons.battery_red
        : ec.level <= low_threshold
        ? Icons.battery_yellow
        : Icons.battery_white;

            p.SetIcon(image, tooltip);
        }
        public void Execute(Vessel v, ScriptType type)
        {
            // do nothing if there is no EC left on the vessel
            Resource_Info ec = ResourceCache.Info(v, "ElectricCharge");

            if (ec.amount <= double.Epsilon)
            {
                return;
            }

            // get the script
            Script script;

            if (scripts.TryGetValue(type, out script))
            {
                // execute the script
                script.Execute(Boot(v));

                // show message to the user
                // - unless the script is empty (can happen when being edited)
                if (script.states.Count > 0 && DB.Vessel(v).cfg_script)
                {
                    Message.Post(Lib.BuildString("Script called on vessel <b>", v.vesselName, "</b>"));
                }
            }
        }
        // return total radiation emitted in a vessel
        public static double Total(Vessel v)
        {
            // get resource cache
            Resource_Info ec = ResourceCache.Info(v, "ElectricCharge");

            double tot = 0.0;

            if (v.loaded)
            {
                foreach (var emitter in Lib.FindModules <Emitter>(v))
                {
                    if (ec.amount > double.Epsilon || emitter.ec_rate <= double.Epsilon)
                    {
                        tot += emitter.running ? emitter.radiation : 0.0;
                    }
                }
            }
            else
            {
                foreach (ProtoPartModuleSnapshot m in Lib.FindModules(v.protoVessel, "Emitter"))
                {
                    if (ec.amount > double.Epsilon || Lib.Proto.GetDouble(m, "ec_rate") <= double.Epsilon)
                    {
                        tot += Lib.Proto.GetBool(m, "running") ? Lib.Proto.GetDouble(m, "radiation") : 0.0;
                    }
                }
            }
            return(tot);
        }
        // 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);
        }
Example #6
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());
            }
        }
Example #7
0
        public override void OnUpdate()
        {
            if (!Lib.IsFlight() || module == null)
            {
                return;
            }

            // get energy from cache
            resources = ResourceCache.Info(vessel, "ElectricCharge");
            hasEnergy = resources.amount > double.Epsilon;

            // Update UI only if hasEnergy has changed or if is broken state has changed
            if (broken)
            {
                if (broken != lastBrokenState)
                {
                    lastBrokenState = broken;
                    Update_UI(!broken);
                }
            }
            else if (hasEnergyChanged != hasEnergy)
            {
                Lib.Debug("Energy state has changed: {0}", hasEnergy);

                // Wait 1 second before enabled UI.
                if (hasEnergy)
                {
                    Lib.Delay(1f);
                }

                hasEnergyChanged = hasEnergy;
                lastBrokenState  = false;
                // Update UI
                Update_UI(hasEnergy);
            }
            // Constantly Update UI for special modules
            if (broken)
            {
                Constant_OnGUI(!broken);
            }
            else
            {
                Constant_OnGUI(hasEnergy);
            }

            if (!hasEnergy || broken)
            {
                actualCost  = 0;
                isConsuming = false;
            }
            else
            {
                isConsuming = GetIsConsuming();
            }
        }
Example #8
0
        public virtual void Update()
        {
            if (Lib.IsFlight() && Features.Deploy)
            {
                // get ec resource handler
                resourceInfo = ResourceCache.Info(vessel, "ElectricCharge");
                hasEC        = resourceInfo.amount > double.Epsilon;

                isConsuming = GetIsConsuming;
                if (!isConsuming)
                {
                    actualECCost = 0;
                }
            }
        }
Example #9
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 a resource handler
        public Resource_Info Info(Vessel v, string resource_name)
        {
            // try to get existing entry if any
            if (resources.TryGetValue(resource_name, out Resource_Info res))
            {
                return(res);
            }

            // create new entry
            res = new Resource_Info(v, resource_name);

            // remember new entry
            resources.Add(resource_name, res);

            // return new entry
            return(res);
        }
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

            // if enabled, and there is ec consumption
            if (running && ec_rate > double.Epsilon)
            {
                // get resource cache
                Resource_Info ec = ResourceCache.Info(vessel, "ElectricCharge");

                // consume EC
                ec.Consume(ec_rate * Kerbalism.elapsed_s);
            }
        }
Example #12
0
        void Indicator_Supplies(Panel p, Vessel v, Vessel_Info vi)
        {
            List <string> tooltips     = new List <string>();
            uint          max_severity = 0;

            if (vi.crew_count > 0)
            {
                foreach (Supply supply in Profile.supplies.FindAll(k => k.resource != "ElectricCharge"))
                {
                    Resource_Info res       = ResourceCache.Info(v, supply.resource);
                    double        depletion = res.Depletion(vi.crew_count);

                    if (res.capacity > double.Epsilon)
                    {
                        if (tooltips.Count == 0)
                        {
                            tooltips.Add("<align=left /><b>name\t\tlevel\tduration</b>");
                        }

                        tooltips.Add(Lib.BuildString
                                     (
                                         res.level <= 0.005 ? "<color=#ff0000>" : res.level <= supply.low_threshold ? "<color=#ffff00>" : "<color=#cccccc>",
                                         supply.resource,
                                         supply.resource != "Ammonia" ? "\t\t" : "\t", //< hack: make ammonia fit damn it
                                         Lib.HumanReadablePerc(res.level), "\t",
                                         depletion <= double.Epsilon ? "depleted" : Lib.HumanReadableDuration(depletion),
                                         "</color>"
                                     ));

                        uint severity = res.level <= 0.005 ? 2u : res.level <= supply.low_threshold ? 1u : 0;
                        max_severity = Math.Max(max_severity, severity);
                    }
                }
            }

            Texture image = max_severity == 2
        ? Icons.box_red
        : max_severity == 1
        ? Icons.box_yellow
        : Icons.box_white;

            p.SetIcon(image, string.Join("\n", tooltips.ToArray()));
        }
Example #13
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);
        }
Example #14
0
        public static void Update(Vessel v)
        {
            // do nothing if not an eva kerbal
            if (!v.isEVA)
            {
                return;
            }

            // get KerbalEVA module
            KerbalEVA kerbal = Lib.FindModules <KerbalEVA>(v)[0];

            // get resource handler
            Resource_Info ec = ResourceCache.Info(v, "ElectricCharge");

            // determine if headlamps need ec
            // - not required if there is no EC capacity in eva kerbal (no ec supply in profile)
            // - not required if no EC cost for headlamps is specified (set by the user)
            bool need_ec = ec.capacity > double.Epsilon && Settings.HeadLampsCost > double.Epsilon;

            // consume EC for the headlamps
            if (need_ec && kerbal.lampOn)
            {
                ec.Consume(Settings.HeadLampsCost * Kerbalism.elapsed_s);
            }

            // force the headlamps on/off
            HeadLamps(kerbal, kerbal.lampOn && (!need_ec || ec.amount > double.Epsilon));

            // if dead
            if (IsDead(v))
            {
                // enforce freezed state
                Freeze(kerbal);

                // disable modules
                DisableModules(kerbal);

                // remove plant flag action
                kerbal.flagItems = 0;
            }
        }
Example #15
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;
            }
        }
Example #16
0
        public void FixedUpdate()
        {
            // in flight
            if (Lib.IsFlight())
            {
                // if we are transmitting using the stock system
                if (stream.Transmitting())
                {
                    // get ec resource handler
                    Resource_Info ec = ResourceCache.Info(vessel, "ElectricCharge");

                    // if we are still linked, and there is ec left
                    if (CanTransmit() && ec.amount > double.Epsilon)
                    {
                        // compression factor
                        // - used to avoid making the user wait too much for transmissions that
                        //   don't happen in background, while keeping transmission rates realistic
                        const double compression = 16.0;

                        // transmit using the data stream
                        stream.Update(DataRate * Kerbalism.elapsed_s * compression, vessel);

                        // consume ec
                        ec.Consume(DataResourceCost * Kerbalism.elapsed_s);
                    }
                    else
                    {
                        // abort transmission, return data to the vessel
                        stream.Abort(vessel);

                        // inform the user
                        ScreenMessages.PostScreenMessage("Transmission aborted", 5.0f, ScreenMessageStyle.UPPER_LEFT);
                    }
                }
            }
        }
Example #17
0
        public void FixedUpdate()
        {
            // do nothing in editor
            if (Lib.IsEditor())
            {
                return;
            }

            // do nothing if there isn't a solar panel
            if (panel == null)
            {
                return;
            }

            // get resource handler
            Resource_Info ec = ResourceCache.Info(vessel, "ElectricCharge");

            // get vessel data from cache
            Vessel_Info info = Cache.VesselInfo(vessel);

            // do nothing if vessel is invalid
            if (!info.is_valid)
            {
                return;
            }

            // detect if sunlight is evaluated analytically
            bool analytical_sunlight = info.sunlight > 0.0 && info.sunlight < 1.0;

            // detect occlusion from other vessel parts
            // - we are only interested when the sunlight evaluation is discrete
            var  collider         = panel.hit.collider;
            bool locally_occluded = !analytical_sunlight && collider != null && info.sunlight > 0.0;

            // if panel is enabled and extended, and if sun is not occluded, not even locally
            if (panel.isEnabled && panel.deployState == ModuleDeployablePart.DeployState.EXTENDED && info.sunlight > 0.0 && !locally_occluded)
            {
                // calculate cosine factor
                // - the stock module is already computing the tracking direction
                double cosine_factor = Math.Max(Vector3d.Dot(info.sun_dir, panel.trackingDotTransform.forward), 0.0);

                // calculate normalized solar flux
                // - this include fractional sunlight if integrated over orbit
                // - this include atmospheric absorption if inside an atmosphere
                double norm_solar_flux = info.solar_flux / Sim.SolarFluxAtHome();

                // calculate output
                double output = rate                      // nominal panel charge rate at 1 AU
                                * norm_solar_flux         // normalized flux at panel distance from sun
                                * cosine_factor;          // cosine factor of panel orientation

                // produce EC
                ec.Produce(output * Kerbalism.elapsed_s);

                // update ui
                field_visibility = info.sunlight * 100.0;
                field_atmosphere = info.atmo_factor * 100.0;
                field_exposure   = cosine_factor * 100.0;
                field_output     = output;
                Fields["field_visibility"].guiActive = analytical_sunlight;
                Fields["field_atmosphere"].guiActive = info.atmo_factor < 1.0;
                Fields["field_exposure"].guiActive   = true;
                Fields["field_output"].guiActive     = true;
            }
            // if panel is disabled, retracted, or in shadow
            else
            {
                // hide ui
                Fields["field_visibility"].guiActive = false;
                Fields["field_atmosphere"].guiActive = false;
                Fields["field_exposure"].guiActive   = false;
                Fields["field_output"].guiActive     = false;
            }

            // update status ui
            field_status = analytical_sunlight
      ? "<color=#ffff22>Integrated over the orbit</color>"
      : locally_occluded
      ? "<color=#ff2222>Occluded by vessel</color>"
      : info.sunlight < 1.0
      ? "<color=#ff2222>Occluded by celestial body</color>"
      : string.Empty;
            Fields["field_status"].guiActive = field_status.Length > 0;
        }
Example #18
0
        static void ProcessRadioisotopeGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule radioisotope_generator, Resource_Info 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);
        }
Example #19
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;
                    }
                }
            }
        }
Example #20
0
 static void ProcessLight(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleLight light, Resource_Info ec, double elapsed_s)
 {
     if (light.useResources && Lib.Proto.GetBool(m, "isOn"))
     {
         ec.Consume(light.resourceAmount * elapsed_s);
     }
 }
Example #21
0
        public void Execute(Vessel v, Vessel_Info vi, Vessel_Resources resources, double elapsed_s)
        {
            // store list of crew to kill
            List <ProtoCrewMember> deferred_kills = new List <ProtoCrewMember>();

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

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

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

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

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

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

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

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

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

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

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

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

                        // if there is no output
                        if (output.Length == 0)
                        {
                            // simply consume (that is faster)
                            res.Consume(required);
                        }
                        // if there is an output
                        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);
            }
        }
Example #22
0
        static void ProcessFissionGenerator(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule fission_generator, Resource_Info 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);
        }
Example #23
0
 public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, AdvancedEC advancedEC, Resource_Info ec, double elapsed_s)
 {
     if (advancedEC.isConsuming)
     {
         ec.Consume(advancedEC.extra_Cost * elapsed_s);
     }
 }
Example #24
0
        static void ProcessScanner(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule scanner, Part part_prefab, VesselData vd, Resource_Info 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);

                // 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
                if (ec.level > 0.25) //< re-enable at 25% EC
                {
                    // 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);
            }
        }
Example #25
0
        public static void BackgroundUpdate(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, Laboratory lab, Resource_Info ec, double elapsed_s)
        {
            // if enabled
            if (Lib.Proto.GetBool(m, "running"))
            {
                // if a researcher is not required, or the researcher is present
                CrewSpecs researcher_cs = new CrewSpecs(lab.researcher);
                int       qtty = 0, crewlvl = 0;
                if (!researcher_cs || researcher_cs.Check(p.protoModuleCrew, out qtty, out crewlvl))
                {
                    // get sample to analyze
                    string sample_filename = Next_sample(v);

                    // if there is a sample to analyze
                    if (sample_filename.Length > 0)
                    {
                        // consume EC
                        ec.Consume(lab.ec_rate * elapsed_s);

                        // if there was ec
                        // - comparing against amount in previous simulation step
                        if (ec.amount > double.Epsilon)
                        {
                            double analysis_rateAVG = lab.analysis_rate;
                            if (researcher_cs)
                            {
                                analysis_rateAVG *= qtty * crewlvl;
                            }
                            // analyze the sample
                            Analyze(v, sample_filename, analysis_rateAVG * elapsed_s);
                        }
                    }
                }
            }
        }
Example #26
0
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

            // if enabled
            if (running)
            {
                int qtty = 0, crewlvl = 0;
                // if a researcher is not required, or the researcher is present
                if (!researcher_cs || researcher_cs.Check(part.protoModuleCrew, out qtty, out crewlvl))
                {
                    // get next sample to analyze
                    string sample_filename = Next_sample(vessel);

                    // if there is a sample to analyze
                    if (sample_filename.Length > 0)
                    {
                        // consume EC
                        Resource_Info ec = ResourceCache.Info(vessel, "ElectricCharge");
                        ec.Consume(ec_rate * Kerbalism.elapsed_s);

                        // if there was ec
                        // - comparing against amount in previous simulation step
                        if (ec.amount > double.Epsilon)
                        {
                            analysis_rateAVG = analysis_rate;
                            if (researcher_cs)
                            {
                                analysis_rateAVG *= qtty * crewlvl;
                            }
                            // analyze the sample
                            Analyze(vessel, sample_filename, analysis_rateAVG * Kerbalism.elapsed_s);

                            //Analyze(vessel, sample_filename, analysis_rate * Kerbalism.elapsed_s);

                            // update status
                            status = Science.Experiment(sample_filename).name;
                        }
                        // if there was no ec
                        else
                        {
                            // update status
                            status = "<color=yellow>no electric charge</color>";
                        }
                    }
                    // if there is no sample to analyze
                    else
                    {
                        // update status
                        status = "no samples to analyze";
                    }
                }
                // if a researcher is required, but missing
                else
                {
                    // update status
                    status = Lib.BuildString("<color=yellow>", researcher_cs.Warning(), "</color>");
                }
            }
            // if disabled
            else
            {
                // update status
                status = "disabled";
            }
        }
Example #27
0
        static void ProcessStockLab(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, ModuleScienceConverter lab, Resource_Info 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);
            }
        }
Example #28
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
            Resource_Info res = null;

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

            // compile list of events with condition satisfied
            List <KerbalBreakdown> events = new List <KerbalBreakdown>();

            events.Add(KerbalBreakdown.mumbling); //< do nothing, here so there is always something that can happen
            if (Lib.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 = "$ON_VESSEL$KERBAL has been in space for too long"; subtext = "Mumbling incoherently"; break;

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

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

            case KerbalBreakdown.wrong_valve: text = "$ON_VESSEL$KERBAL opened the wrong valve"; subtext = res.resource_name + " 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); break;
            }

            // remove reputation
            if (HighLogic.CurrentGame.Mode == Game.Modes.CAREER)
            {
                Reputation.Instance.AddReputation(-Settings.BreakdownReputation, TransactionReasons.Any);
            }
        }
Example #29
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>"));
                }
            }
        }
Example #30
0
        static void ProcessCurvedPanel(Vessel v, ProtoPartSnapshot p, ProtoPartModuleSnapshot m, PartModule curved_panel, Part part_prefab, Vessel_Info info, Resource_Info ec, double elapsed_s)
        {
            // note: we assume deployed, this is a current limitation

            // if in sunlight
            if (info.sunlight > double.Epsilon)
            {
                // get values from module
                string transform_name = Lib.ReflectionValue <string>(curved_panel, "PanelTransformName");
                float  tot_rate       = Lib.ReflectionValue <float>(curved_panel, "TotalEnergyRate");

                // get components
                Transform[] components = part_prefab.FindModelTransforms(transform_name);
                if (components.Length == 0)
                {
                    return;
                }

                // calculate normalized solar flux
                // note: this include fractional sunlight if integrated over orbit
                // note: this include atmospheric absorption if inside an atmosphere
                double norm_solar_flux = info.solar_flux / Sim.SolarFluxAtHome();

                // calculate rate per component
                double rate = (double)tot_rate / (double)components.Length;

                // calculate world-space part rotation quaternion
                // note: a possible optimization here is to cache the transform lookup (unity was coded by monkeys)
                Quaternion rot = v.transform.rotation * p.rotation;

                // calculate output of all components
                double output = 0.0;
                foreach (Transform t in components)
                {
                    output += rate                                                                       // nominal rate per-component at 1 AU
                              * norm_solar_flux                                                          // normalized solar flux at panel distance from sun
                              * Math.Max(Vector3d.Dot(info.sun_dir, (rot * t.forward).normalized), 0.0); // cosine factor of component orientation
                }

                // produce EC
                ec.Produce(output * elapsed_s);
            }
        }