Пример #1
0
        public override void OnStart(StartState state)
        {
            // don't break tutorial scenarios
            if (Lib.DisableScenario(this))
            {
                return;
            }

            // parse all setups from string data
            var archive = new ReadArchive(data);
            int count;

            archive.Load(out count);
            setups = new List <ConfigureSetup>(count);
            while (count-- > 0)
            {
                setups.Add(new ConfigureSetup(archive));
            }

            // parse configuration from string data
            // - we avoid corner case when cfg was never set up (because craft was never in VAB)
            selected = new List <string>();
            if (!string.IsNullOrEmpty(cfg))
            {
                archive = new ReadArchive(cfg);
                archive.Load(out count);
                while (count-- > 0)
                {
                    string s; archive.Load(out s); selected.Add(s);
                }
            }

            // parse previous configuration from string data
            // - we avoid corner case when prev_cfg was never set up (because craft was never in VAB)
            prev_selected = new List <string>();
            if (!string.IsNullOrEmpty(prev_cfg))
            {
                archive = new ReadArchive(prev_cfg);
                archive.Load(out count);
                while (count-- > 0)
                {
                    string s; archive.Load(out s); prev_selected.Add(s);
                }
            }

            // default title to part name
            if (title.Length == 0)
            {
                title = Lib.PartName(part);
            }

            // parse crew specs
            reconfigure_cs = new CrewSpecs(reconfigure);

            // set toggle window button label
            Events["ToggleWindow"].guiName = Lib.BuildString("Configure <b>", title, "</b>");

            // only show toggle in flight if this is reconfigurable
            Events["ToggleWindow"].active = Lib.IsEditor() || reconfigure_cs;

            // store configuration changes
            changes = new Dictionary <int, int>();
        }
Пример #2
0
        void Problem_Kerbals(List <ProtoCrewMember> crew, ref List <Texture> icons, ref List <string> tooltips)
        {
            UInt32 health_severity = 0;
            UInt32 stress_severity = 0;

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

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

                foreach (Rule r in Profile.rules)
                {
                    RuleData rd = kd.Rule(r.name);
                    if (rd.problem > r.danger_threshold)
                    {
                        if (!r.breakdown)
                        {
                            health_severity = Math.Max(health_severity, 2);
                        }
                        else
                        {
                            stress_severity = Math.Max(stress_severity, 2);
                        }
                        tooltips.Add(Lib.BuildString(c.name, ": <b>", r.name, "</b>"));
                    }
                    else if (rd.problem > r.warning_threshold)
                    {
                        if (!r.breakdown)
                        {
                            health_severity = Math.Max(health_severity, 1);
                        }
                        else
                        {
                            stress_severity = Math.Max(stress_severity, 1);
                        }
                        tooltips.Add(Lib.BuildString(c.name, ": <b>", r.name, "</b>"));
                    }
                }
            }
            if (health_severity == 1)
            {
                icons.Add(Icons.health_yellow);
            }
            else if (health_severity == 2)
            {
                icons.Add(Icons.health_red);
            }
            if (stress_severity == 1)
            {
                icons.Add(Icons.brain_yellow);
            }
            else if (stress_severity == 2)
            {
                icons.Add(Icons.brain_red);
            }
        }
Пример #3
0
        public void FixedUpdate()
        {
            // do nothing in the editor
            if (Lib.IsEditor())
            {
                return;
            }

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

                // get resource cache
                Vessel_Resources resources = ResourceCache.Get(vessel);
                Resource_Info    ec        = resources.Info(vessel, "ElectricCharge");

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

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

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

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

                // execute recipe
                Resource_Recipe recipe = new Resource_Recipe();
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    recipe.Input(input.name, input.rate * Kerbalism.elapsed_s);
                }
                foreach (ModuleResource output in resHandler.outputResources)
                {
                    recipe.Output(output.name, output.rate * Kerbalism.elapsed_s, true);
                }
                resources.Transform(recipe);

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

                // determine input resources conditions
                // - comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                foreach (ModuleResource input in resHandler.inputResources)
                {
                    if (resources.Info(vessel, input.name).amount <= double.Epsilon)
                    {
                        inputs      = false;
                        missing_res = input.name;
                        break;
                    }
                }

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

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

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

                // update issues
                issue =
                    !inputs?Lib.BuildString("missing ", missing_res)
                        : !lighting  ? "insufficient lighting"
        : !pressure  ? "insufficient pressure"
        : !radiation ? "excessive radiation"
        : string.Empty;
            }
        }
Пример #4
0
        public static void PartPrefabsTweaks()
        {
            foreach (AvailablePart ap in PartLoader.LoadedPartsList)
            {
                // scale part icons of the radial container variants
                switch (ap.name)
                {
                case "kerbalism-container-radial-small":
                    ap.iconPrefab.transform.GetChild(0).localScale *= 0.60f;
                    ap.iconScale *= 0.60f;
                    break;

                case "kerbalism-container-radial-medium":
                    ap.iconPrefab.transform.GetChild(0).localScale *= 0.85f;
                    ap.iconScale *= 0.85f;
                    break;

                case "kerbalism-container-radial-big":
                    ap.iconPrefab.transform.GetChild(0).localScale *= 1.10f;
                    ap.iconScale *= 1.10f;
                    break;

                case "kerbalism-container-radial-huge":
                    ap.iconPrefab.transform.GetChild(0).localScale *= 1.33f;
                    ap.iconScale *= 1.33f;
                    break;

                case "kerbalism-container-inline-375":
                    ap.iconPrefab.transform.GetChild(0).localScale *= 1.33f;
                    ap.iconScale *= 1.33f;
                    break;
                }

                // force a non-lexical order in the editor
                switch (ap.name)
                {
                case "kerbalism-container-inline-0625":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>00</color></size>", ap.title);
                    break;

                case "kerbalism-container-inline-125":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>01</color></size>", ap.title);
                    break;

                case "kerbalism-container-inline-250":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>02</color></size>", ap.title);
                    break;

                case "kerbalism-container-inline-375":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>03</color></size>", ap.title);
                    break;

                case "kerbalism-container-radial-small":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>04</color></size>", ap.title);
                    break;

                case "kerbalism-container-radial-medium":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>05</color></size>", ap.title);
                    break;

                case "kerbalism-container-radial-big":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>06</color></size>", ap.title);
                    break;

                case "kerbalism-container-radial-huge":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>07</color></size>", ap.title);
                    break;

                case "kerbalism-greenhouse":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>08</color></size>", ap.title);
                    break;

                case "kerbalism-gravityring":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>09</color></size>", ap.title);
                    break;

                case "kerbalism-activeshield":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>10</color></size>", ap.title);
                    break;

                case "kerbalism-chemicalplant":
                    ap.title = Lib.BuildString("<size=1><color=#00000000>11</color></size>", ap.title);
                    break;
                }

                // recompile some part infos (this is normally done by KSP on loading, after each part prefab is compiled)
                // This is needed because :
                // - We can't check interdependent modules when OnLoad() is called, since the other modules may not be loaded yet
                // - The science DB needs the system/bodies to be instantiated, which is done after the part compilation
                bool partNeedsInfoRecompile = false;

                foreach (PartModule module in ap.partPrefab.Modules)
                {
                    // we want to remove the editor part tooltip module infos widgets that are switchable trough the configure module
                    // because the clutter the UI quite a bit. To do so, every module that implements IConfigurable is made to return
                    // an empty string in their GetInfo() if the IConfigurable.ModuleIsConfigured() is ever called on them.
                    if (module is Configure configure)
                    {
                        List <IConfigurable> configurables = configure.GetIConfigurableModules();

                        if (configurables.Count > 0)
                        {
                            partNeedsInfoRecompile = true;
                        }

                        foreach (IConfigurable configurable in configurables)
                        {
                            configurable.ModuleIsConfigured();
                        }
                    }
                    // note that the experiment modules on the prefab gets initialized from the scienceDB init, which also do
                    // a LoadedPartsList loop to get the scienceDB module infos. So this has to be called after the scienceDB init.
                    else if (module is Experiment)
                    {
                        partNeedsInfoRecompile = true;
                    }
                }

                // for some reason this crashes on the EVA kerbals parts
                if (partNeedsInfoRecompile && !ap.name.StartsWith("kerbalEVA"))
                {
                    ap.moduleInfos.Clear();
                    ap.resourceInfos.Clear();
                    try
                    {
                        Lib.ReflectionCall(PartLoader.Instance, "CompilePartInfo", new Type[] { typeof(AvailablePart), typeof(Part) }, new object[] { ap, ap.partPrefab });
                    }
                    catch (Exception ex)
                    {
                        Lib.Log("Could not patch the moduleInfo for part " + ap.name + " - " + ex.Message + "\n" + ex.StackTrace);
                    }
                }
            }
        }
Пример #5
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);
            }
        }
Пример #6
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;
            bool          drive_full     = vi.free_capacity < double.MaxValue && (vi.free_capacity / vi.total_capacity < 0.15);
            bool          drive_empty    = vi.free_capacity >= double.MaxValue || (vi.free_capacity / vi.total_capacity > 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
                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>"));
                }
            }
        }
Пример #7
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>"));
                    }
                }
            }
        }
Пример #8
0
        }                                                                              //< make sure it is at the top

        public string GetPrimaryField()
        {
            return(Lib.BuildString("Configurable ", title));
        }
Пример #9
0
        public void generate_details(Configure cfg)
        {
            // If a setup component is defined after the Configure module in the ConfigNode,
            // then it is not present in the part during Configure::OnLoad (at precompilation time)
            // so, find_module() will fail in that situation, resulting in no component details
            // being added to the Configure window. Therefore we are forced to generate the details
            // at first use every time the module is loaded, instead of generating them only once.

            // already generated
            if (details != null)
            {
                return;
            }

            // generate module details
            details = new List <Detail>();
            foreach (ConfigureModule cm in modules)
            {
                // find module, skip if it doesn't exist
                PartModule m = cfg.find_module(cm);
                if (m == null)
                {
                    continue;
                }

                // get title
                IModuleInfo module_info = m as IModuleInfo;
                string      title       = module_info != null?module_info.GetModuleTitle() : cm.type;

                if (title.Length == 0)
                {
                    continue;
                }

                // get specs, skip if not implemented by module
                ISpecifics specifics = m as ISpecifics;
                if (specifics == null)
                {
                    continue;
                }
                Specifics specs = specifics.Specs();
                if (specs.entries.Count == 0)
                {
                    continue;
                }

                // add title to details
                details.Add(new Detail(Lib.BuildString("<b><color=#00ffff>", title, "</color></b>")));

                // add specs to details
                foreach (Specifics.Entry e in specs.entries)
                {
                    details.Add(new Detail(e.label, e.value));
                }
            }

            // get visible resources subset
            List <ConfigureResource> visible_resources = resources.FindAll(k => Lib.GetDefinition(k.name).isVisible);

            // generate resource details
            if (visible_resources.Count > 0)
            {
                // add resources title
                details.Add(new Detail("<b><color=#00ffff>Resources</color></b>"));

                // for each visible resource
                foreach (ConfigureResource cr in visible_resources)
                {
                    // add capacity info
                    details.Add(new Detail(cr.name, Lib.Parse.ToDouble(cr.maxAmount).ToString("F2")));
                }
            }

            // generate extra details
            if (mass > double.Epsilon || cost > double.Epsilon)
            {
                details.Add(new Detail("<b><color=#00ffff>Extra</color></b>"));
                if (mass > double.Epsilon)
                {
                    details.Add(new Detail("mass", Lib.HumanReadableMass(mass)));
                }
                if (cost > double.Epsilon)
                {
                    details.Add(new Detail("cost", Lib.HumanReadableCost(cost)));
                }
            }
        }
Пример #10
0
        public static void Update(Vessel v, Vessel_Info vi, VesselData vd, double elapsed_s)
        {
            // do nothing if signal mechanic is disabled
            if (!Features.Signal && !Features.KCommNet && !RemoteTech.Enabled())
            {
                return;
            }

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

            // maintain and send messages
            // - do not send messages for vessels without an antenna
            // - do not send messages during/after solar storms
            // - do not send messages for EVA kerbals
            if (conn.status != LinkStatus.no_antenna && !v.isEVA && v.situation != Vessel.Situations.PRELAUNCH)
            {
                if (!vd.msg_signal && !conn.linked)
                {
                    vd.msg_signal = true;
                    if (vd.cfg_signal && conn.status != LinkStatus.blackout)
                    {
                        string subtext = "Data transmission disabled";
                        if (vi.crew_count == 0)
                        {
                            switch (Settings.UnlinkedControl)
                            {
                            case UnlinkedCtrl.none: subtext = "Remote control disabled"; break;

                            case UnlinkedCtrl.limited: subtext = "Limited control available"; break;
                            }
                        }
                        Message.Post(Severity.warning, Lib.BuildString("Signal lost with <b>", v.vesselName, "</b>"), subtext);
                    }
                }
                else if (vd.msg_signal && conn.linked)
                {
                    vd.msg_signal = false;
                    if (vd.cfg_signal && !Storm.JustEnded(v, elapsed_s))
                    {
                        var path = conn.path;
                        Message.Post(Severity.relax, Lib.BuildString("<b>", v.vesselName, "</b> signal is back"),
                                     path.Count == 0 ? "We got a direct link with the space center" : Lib.BuildString("Relayed by <b>", path[path.Count - 1].vesselName, "</b>"));
                    }
                }
            }
        }
Пример #11
0
 // module info support
 public string GetModuleTitle()
 {
     return(Lib.BuildString("# Configurable ", title));
 }                                                                              //< make sure it is at the top
Пример #12
0
  public void update(CelestialBody body, double elapsed_s)
  {
    // do nothing if storms are disabled
    if (Settings.StormDuration <= double.Epsilon) return;

    // skip the sun
    if (body.flightGlobalsIndex == 0) return;

    // skip moons
    // note: referenceBody is never null here
    if (body.referenceBody.flightGlobalsIndex != 0) return;

    // get body data
    body_data bd = DB.BodyData(body.name);

    // generate storm time if necessary
    if (bd.storm_time <= double.Epsilon)
    {
      bd.storm_time = Settings.StormMinTime + (Settings.StormMaxTime - Settings.StormMinTime) * Lib.RandomDouble();
    }

    // accumulate age
    bd.storm_age += elapsed_s * storm_frequency(body.orbit.semiMajorAxis);

    // if storm is over
    if (bd.storm_age > bd.storm_time)
    {
      bd.storm_age = 0.0;
      bd.storm_time = 0.0;
      bd.storm_state = 0;
    }
    // if storm is in progress
    else if (bd.storm_age > bd.storm_time - Settings.StormDuration)
    {
      bd.storm_state = 2;
    }
    // if storm is incoming
    else if (bd.storm_age > bd.storm_time - Settings.StormDuration  - time_to_impact(body.orbit.semiMajorAxis))
    {
      bd.storm_state = 1;
    }

    // send messages
    // note: separed from state management to support the case when the user enter the SOI of a body under storm or about to be hit
    if (bd.msg_storm < 2 && bd.storm_state == 2)
    {
      if (body_is_relevant(body))
      {
        Message.Post(Severity.danger, Lib.BuildString("The coronal mass ejection hit <b>", body.name, "</b> system"),
          Lib.BuildString("Storm duration: ", Lib.HumanReadableDuration(TimeLeftCME(bd.storm_time, bd.storm_age))));
      }
      bd.msg_storm = 2;
    }
    else if (bd.msg_storm < 1 && bd.storm_state == 1)
    {
      if (body_is_relevant(body))
      {
        Message.Post(Severity.warning, Lib.BuildString("Our observatories report a coronal mass ejection directed toward <b>", body.name, "</b> system"),
          Lib.BuildString("Time to impact: ", Lib.HumanReadableDuration(TimeBeforeCME(bd.storm_time, bd.storm_age))));
      }
      bd.msg_storm = 1;
    }
    else if (bd.msg_storm > 1 && bd.storm_state == 0)
    {
      if (body_is_relevant(body))
      {
        Message.Post(Severity.relax, Lib.BuildString("The solar storm at <b>", body.name, "</b> system is over"));
      }
      bd.msg_storm = 0;
    }
  }
Пример #13
0
        }                                                                                                                                                   // attempt to display at the top

        public string GetPrimaryField()
        {
            return(Lib.BuildString("<size=1><color=#00000000>00</color></size>Configurable ", title));
        }                                                                                                                                     // attempt to display at the top
Пример #14
0
        }                                                                                                                                    // attempt to display at the top

        public override string GetModuleDisplayName()
        {
            return(Lib.BuildString("<size=1><color=#00000000>00</color></size>Configurable ", title));
        }                                                                                                                                                   // attempt to display at the top
Пример #15
0
 public override string ToString()
 {
     return(Lib.BuildString(BodyName, ScienceSituationName, BiomeName));
 }
Пример #16
0
        public void Update()
        {
            // in flight
            if (Lib.IsFlight())
            {
                // get info from cache
                vessel_info vi = Cache.VesselInfo(vessel);

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

                // update ui
                bool has_operator = operator_cs.check(vessel);
                Events["Toggle"].guiName = Lib.StatusToggle(exp_name, !recording ? "stopped" : issue.Length == 0 ? "recording" : Lib.BuildString("<color=#ffff00>", issue, "</color>"));
            }
            // in the editor
            else if (Lib.IsEditor())
            {
                // update ui
                Events["Toggle"].guiName = Lib.StatusToggle(exp_name, recording ? "recording" : "stopped");
            }
        }
Пример #17
0
		public static void body_info(this Panel p)
		{
			// only show in mapview
			if (!MapView.MapIsEnabled) return;

			// only show if there is a selected body and that body is not the sun
			CelestialBody body = Lib.SelectedBody();
			if (body == null || (body.flightGlobalsIndex == 0 && !Features.Radiation)) return;

			// shortcut
			CelestialBody sun = FlightGlobals.Bodies[0];

			// for all bodies except the sun
			if (body != sun)
			{
				// calculate simulation values
				double atmo_factor = Sim.AtmosphereFactor(body, 0.7071);
				double gamma_factor = Sim.GammaTransparency(body, 0.0);
				double sun_dist = Sim.Apoapsis(Lib.PlanetarySystem(body)) - sun.Radius - body.Radius;
				Vector3d sun_dir = (sun.position - body.position).normalized;
				double solar_flux = Sim.SolarFlux(sun_dist) * atmo_factor;
				double albedo_flux = Sim.AlbedoFlux(body, body.position + sun_dir * body.Radius);
				double body_flux = Sim.BodyFlux(body, 0.0);
				double total_flux = solar_flux + albedo_flux + body_flux + Sim.BackgroundFlux();
				double temperature = body.atmosphere ? body.GetTemperature(0.0) : Sim.BlackBodyTemperature(total_flux);

				// calculate night-side temperature
				double total_flux_min = Sim.AlbedoFlux(body, body.position - sun_dir * body.Radius) + body_flux + Sim.BackgroundFlux();
				double temperature_min = Sim.BlackBodyTemperature(total_flux_min);

				// calculate radiation at body surface
				double radiation = Radiation.ComputeSurface(body, gamma_factor);

				// surface panel
				string temperature_str = body.atmosphere
				  ? Lib.HumanReadableTemp(temperature)
				  : Lib.BuildString(Lib.HumanReadableTemp(temperature_min), " / ", Lib.HumanReadableTemp(temperature));
				p.section("SURFACE");
				p.content("temperature", temperature_str);
				p.content("solar flux", Lib.HumanReadableFlux(solar_flux));
				if (Features.Radiation) p.content("radiation", Lib.HumanReadableRadiation(radiation));

				// atmosphere panel
				if (body.atmosphere)
				{
					p.section("ATMOSPHERE");
					p.content("breathable", Sim.Breathable(body) ? "yes" : "no");
					p.content("light absorption", Lib.HumanReadablePerc(1.0 - Sim.AtmosphereFactor(body, 0.7071)));
					if (Features.Radiation) p.content("gamma absorption", Lib.HumanReadablePerc(1.0 - Sim.GammaTransparency(body, 0.0)));
				}
			}

			// rendering panel
			if (Features.Radiation)
			{
				p.section("RENDERING");
				p.content("inner belt", Radiation.show_inner ? "<color=green>show</color>" : "<color=red>hide</color>", string.Empty, () => p.toggle(ref Radiation.show_inner));
				p.content("outer belt", Radiation.show_outer ? "<color=green>show</color>" : "<color=red>hide</color>", string.Empty, () => p.toggle(ref Radiation.show_outer));
				p.content("magnetopause", Radiation.show_pause ? "<color=green>show</color>" : "<color=red>hide</color>", string.Empty, () => p.toggle(ref Radiation.show_pause));
			}

			// explain the user how to toggle the BodyInfo window
			p.content(string.Empty);
			p.content("<i>Press <b>B</b> to open this window again</i>");

			// set metadata
			p.title(Lib.BuildString(Lib.Ellipsis(body.bodyName, Styles.ScaleStringLength(24)), " <color=#cccccc>BODY INFO</color>"));
		}
Пример #18
0
        /// <summary>synchronize resources from cache to vessel</summary>
        /// <remarks>
        /// this function will also sync from vessel to cache so you can always use the
        /// ResourceInfo interface to get information about resources
        /// </remarks>
        public void Sync(Vessel v, VesselData vd, double elapsed_s, List <PartResource> loadedResList, List <ProtoPartResourceSnapshot> unloadedResList)
        {
            UnityEngine.Profiling.Profiler.BeginSample("Kerbalism.Resource.Sync");
            // # OVERVIEW
            // - consumption/production is accumulated in "Deferred", then this function called
            // - save previous step amount/capacity
            // - part loop 1 : detect new amount/capacity
            // - if amount has changed, this mean there is non-Kerbalism producers/consumers on the vessel
            // - if non-Kerbalism producers are detected on a loaded vessel, prevent high timewarp rates
            // - clamp "Deferred" to amount/capacity
            // - part loop 2 : apply "Deferred" to all parts
            // - apply "Deferred" to amount
            // - calculate change rate per-second
            // - calculate resource level
            // - reset deferred

            // # NOTE
            // It is impossible to guarantee coherency in resource simulation of loaded vessels,
            // if consumers/producers external to the resource cache exist in the vessel (#96).
            // Such is the case for example on loaded vessels with stock solar panels.
            // The effect is that the whole resource simulation become dependent on timestep again.
            // From the user point-of-view, there are two cases:
            // - (A) the timestep-dependent error is smaller than capacity
            // - (B) the timestep-dependent error is bigger than capacity
            // In case [A], there are no consequences except a slightly wrong computed level and rate.
            // In case [B], the simulation became incoherent and from that point anything can happen,
            // like for example insta-death by co2 poisoning or climatization.
            // To avoid the consequences of [B]:
            // - we hacked the solar panels to use the resource cache (SolarPanelFixer)
            // - we detect incoherency on loaded vessels, and forbid the two highest warp speeds

            // remember vessel-wide amount currently known, to calculate rate and detect non-Kerbalism brokers
            double oldAmount = Amount;

            // remember vessel-wide capacity currently known, to detect flow state changes
            double oldCapacity = Capacity;

            // iterate over all enabled resource containers and detect amount/capacity again
            // - this detect production/consumption from stock and third-party mods
            //   that by-pass the resource cache, and flow state changes in general
            Amount   = 0.0;
            Capacity = 0.0;

            if (v.loaded)
            {
                foreach (PartResource r in loadedResList)
                {
                    Amount   += r.amount;
                    Capacity += r.maxAmount;
                }
            }
            else
            {
                foreach (ProtoPartResourceSnapshot r in unloadedResList)
                {
                    Amount   += r.amount;
                    Capacity += r.maxAmount;
                }
            }

            // As we haven't yet synchronized anything, changes to amount can only come from non-Kerbalism producers or consumers
            double unsupportedBrokersRate = Amount - oldAmount;

            // Avoid false detection due to precision errors
            if (Math.Abs(unsupportedBrokersRate) < 1e-05)
            {
                unsupportedBrokersRate = 0.0;
            }
            // Calculate the resulting rate
            unsupportedBrokersRate /= elapsed_s;

            // Detect flow state changes
            bool flowStateChanged = Capacity - oldCapacity > 1e-05;

            // clamp consumption/production to vessel amount/capacity
            // - if deferred is negative, then amount is guaranteed to be greater than zero
            // - if deferred is positive, then capacity - amount is guaranteed to be greater than zero
            Deferred = Lib.Clamp(Deferred, -Amount, Capacity - Amount);

            // apply deferred consumption/production to all parts, simulating ALL_VESSEL_BALANCED
            // - iterating again is faster than using a temporary list of valid PartResources
            // - avoid very small values in deferred consumption/production
            if (Math.Abs(Deferred) > 1e-10)
            {
                if (v.loaded)
                {
                    foreach (PartResource r in loadedResList)
                    {
                        // calculate consumption/production coefficient for the part
                        double k = Deferred < 0.0
                                                  ? r.amount / Amount
                                                  : (r.maxAmount - r.amount) / (Capacity - Amount);

                        // apply deferred consumption/production
                        r.amount += Deferred * k;
                    }
                }
                else
                {
                    foreach (ProtoPartResourceSnapshot r in unloadedResList)
                    {
                        // calculate consumption/production coefficient for the part
                        double k = Deferred < 0.0
                                                  ? r.amount / Amount
                                                  : (r.maxAmount - r.amount) / (Capacity - Amount);

                        // apply deferred consumption/production
                        r.amount += Deferred * k;
                    }
                }
            }

            // update amount, to get correct rate and levels at all times
            Amount += Deferred;

            // reset deferred production/consumption
            Deferred = 0.0;

            // recalculate level
            Level = Capacity > 0.0 ? Amount / Capacity : 0.0;

            // calculate rate of change per-second
            // - don't update rate during warp blending (stock modules have instabilities during warp blending)
            // - ignore interval-based rules consumption/production
            if (!v.loaded || !Kerbalism.WarpBlending)
            {
                Rate = (Amount - oldAmount - intervalRuleAmount) / elapsed_s;
            }

            // calculate average rate of change per-second from interval-based rules
            intervalRulesRate = 0.0;
            foreach (var rb in intervalRuleBrokersRates)
            {
                intervalRulesRate += rb.Value;
            }

            // AverageRate is the exposed property that include simulated rate from interval-based rules.
            // For consistency with how "Rate" is calculated, we only add the simulated rate if there is some capacity or amount for it to have an effect
            AverageRate = Rate;
            if ((intervalRulesRate > 0.0 && Level < 1.0) || (intervalRulesRate < 0.0 && Level > 0.0))
            {
                AverageRate += intervalRulesRate;
            }

            // For visualization purpose, update the VesselData.supplies brokers list, merging all detected sources :
            // - normal brokers that use Consume() or Produce()
            // - "virtual" brokers from interval-based rules
            // - non-Kerbalism brokers (aggregated rate)
            vd.Supply(ResourceName).UpdateResourceBrokers(brokersResourceAmounts, intervalRuleBrokersRates, unsupportedBrokersRate, elapsed_s);

            //Lib.Log("RESOURCE UPDATE : " + v);
            //foreach (var rb in vd.Supply(ResourceName).ResourceBrokers)
            //	Lib.Log(Lib.BuildString(ResourceName, " : ", rb.rate.ToString("+0.000000;-0.000000;+0.000000"), "/s (", rb.name, ")"));
            //Lib.Log("RESOURCE UPDATE END");

            // reset amount added/removed from interval-based rules
            IntervalRuleHappened = intervalRuleAmount > 0.0;
            intervalRuleAmount   = 0.0;

            // if incoherent producers are detected, do not allow high timewarp speed
            // - can be disabled in settings
            // - unloaded vessels can't be incoherent, we are in full control there
            // - ignore incoherent consumers (no negative consequences for player)
            // - ignore flow state changes (avoid issue with process controllers and other things)
            if (Settings.EnforceCoherency && v.loaded && TimeWarp.CurrentRate > 1000.0 && unsupportedBrokersRate > 0.0 && !flowStateChanged)
            {
                Message.Post
                (
                    Severity.warning,
                    Lib.BuildString
                    (
                        !v.isActiveVessel ? Lib.BuildString("On <b>", v.vesselName, "</b>\na ") : "A ",
                        "producer of <b>", ResourceName, "</b> has\n",
                        "incoherent behavior at high warp speed.\n",
                        "<i>Unload the vessel before warping</i>"
                    )
                );
                Lib.StopWarp(1000.0);
            }

            // reset brokers
            brokersResourceAmounts.Clear();
            intervalRuleBrokersRates.Clear();

            // reset amount added/removed from interval-based rules
            intervalRuleAmount = 0.0;
            UnityEngine.Profiling.Profiler.EndSample();
        }
Пример #19
0
 public void Update()
 {
     // update ui
     Status = deployed ? Lib.BuildString(Local.PassiveShield_absorbing, " ", Lib.HumanReadableRadiation(Math.Abs(radiation))) : disabledTitle;            //"absorbing
     Events["Toggle"].guiName = Lib.StatusToggle(title, deployed ? disengageActionTitle : engageActionTitle);
 }
Пример #20
0
        // synchronize amount from cache to vessel
        public void Sync(Vessel v, double elapsed_s)
        {
            // # OVERVIEW
            // - deferred consumption/production is accumulated, then this function called
            // - detect amount/capacity in vessel
            // - clamp deferred to amount/capacity
            // - apply deferred
            // - update cached amount [disabled, see comments]
            // - calculate change rate per-second
            // - calculate resource level
            // - reset deferred

            // # NOTE
            // It is impossible to guarantee coherency in resource simulation of loaded vessels,
            // if consumers/producers external to the resource cache exist in the vessel (#96).
            // Such is the case for example on loaded vessels with stock solar panels.
            // The effect is that the whole resource simulation become dependent on timestep again.
            // From the user point-of-view, there are two cases:
            // - (A) the timestep-dependent error is smaller than capacity
            // - (B) the timestep-dependent error is bigger than capacity
            // In case [A], there are no consequences except a slightly wrong computed level and rate.
            // In case [B], the simulation became incoherent and from that point anything can happen,
            // like for example insta-death by co2 poisoning or climatization.
            // To avoid the consequences of [B]:
            // - we hacked the stock solar panel to use the resource cache
            // - we detect incoherency on loaded vessels, and forbid the two highest warp speeds


            // remember amount currently known, to calculate rate later on
            double old_amount = amount;

            // remember capacity currently known, to detect flow state changes
            double old_capacity = capacity;

            // iterate over all enabled resource containers and detect amount/capacity again
            // - this detect production/consumption from stock and third-party mods
            //   that by-pass the resource cache, and flow state changes in general
            amount   = 0.0;
            capacity = 0.0;
            if (v.loaded)
            {
                foreach (Part p in v.Parts)
                {
                    foreach (PartResource r in p.Resources)
                    {
                        if (r.flowState && r.resourceName == resource_name)
                        {
                            amount   += r.amount;
                            capacity += r.maxAmount;
                        }
                    }
                }
            }
            else
            {
                foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
                {
                    foreach (ProtoPartResourceSnapshot r in p.resources)
                    {
                        if (r.flowState && r.resourceName == resource_name)
                        {
                            amount   += r.amount;
                            capacity += r.maxAmount;
                        }
                    }
                }
            }

            // if incoherent producers are detected, do not allow high timewarp speed
            // - ignore incoherent consumers (no negative consequences for player)
            // - ignore flow state changes (avoid issue with process controllers)
            // - unloaded vessels can't be incoherent, we are in full control there
            // - can be disabled in settings
            // - avoid false detection due to precision errors in stock amounts
            if (Settings.EnforceCoherency && v.loaded && TimeWarp.CurrentRateIndex >= 6 && amount - old_amount > 1e-05 && capacity - old_capacity < 1e-05)
            {
                Message.Post
                (
                    Severity.warning,
                    Lib.BuildString
                    (
                        !v.isActiveVessel ? Lib.BuildString("On <b>", v.vesselName, "</b>\na ") : "A ",
                        "producer of <b>", resource_name, "</b> has\n",
                        "incoherent behaviour at high warp speed.\n",
                        "<i>Unload the vessel before warping</i>"
                    )
                );
                Lib.StopWarp(5);
            }


            // clamp consumption/production to vessel amount/capacity
            // - if deferred is negative, then amount is guaranteed to be greater than zero
            // - if deferred is positive, then capacity - amount is guaranteed to be greater than zero
            deferred = Lib.Clamp(deferred, -amount, capacity - amount);

            // apply deferred consumption/production, simulating ALL_VESSEL_BALANCED
            // - iterating again is faster than using a temporary list of valid PartResources
            // - avoid very small values in deferred consumption/production
            if (Math.Abs(deferred) > 1e-10)
            {
                if (v.loaded)
                {
                    foreach (Part p in v.parts)
                    {
                        foreach (PartResource r in p.Resources)
                        {
                            if (r.flowState && r.resourceName == resource_name)
                            {
                                // calculate consumption/production coefficient for the part
                                double k = deferred < 0.0
                ? r.amount / amount
                : (r.maxAmount - r.amount) / (capacity - amount);

                                // apply deferred consumption/production
                                r.amount += deferred * k;
                            }
                        }
                    }
                }
                else
                {
                    foreach (ProtoPartSnapshot p in v.protoVessel.protoPartSnapshots)
                    {
                        foreach (ProtoPartResourceSnapshot r in p.resources)
                        {
                            if (r.flowState && r.resourceName == resource_name)
                            {
                                // calculate consumption/production coefficient for the part
                                double k = deferred < 0.0
                ? r.amount / amount
                : (r.maxAmount - r.amount) / (capacity - amount);

                                // apply deferred consumption/production
                                r.amount += deferred * k;
                            }
                        }
                    }
                }
            }

            // update amount, to get correct rate and levels at all times
            amount += deferred;

            // calculate rate of change per-second
            // - don't update rate during and immediately after warp blending (stock modules have instabilities during warp blending)
            // - don't update rate during the simulation steps where meal is consumed, to avoid counting it twice
            if ((!v.loaded || Kerbalism.warp_blending > 50) && !meal_happened)
            {
                rate = (amount - old_amount) / elapsed_s;
            }

            // recalculate level
            level = capacity > double.Epsilon ? amount / capacity : 0.0;

            // reset deferred production/consumption
            deferred = 0.0;

            // reset meal flag
            meal_happened = false;
        }
Пример #21
0
        public static void ManageRescueMission(Vessel v)
        {
            // true if we detected this was a rescue mission vessel
            bool detected = false;

            // deal with rescue missions
            foreach (ProtoCrewMember c in Lib.CrewList(v))
            {
                // get kerbal data
                KerbalData kd = DB.Kerbal(c.name);

                // flag the kerbal as not rescue at prelaunch
                if (v.situation == Vessel.Situations.PRELAUNCH)
                {
                    kd.rescue = false;
                }

                // if the kerbal belong to a rescue mission
                if (kd.rescue)
                {
                    // remember it
                    detected = true;

                    // flag the kerbal as non-rescue
                    // note: enable life support mechanics for the kerbal
                    kd.rescue = false;

                    // show a message
                    Message.Post(Lib.BuildString(Local.Rescuemission_msg1, " <b>", c.name, "</b>"), Lib.BuildString((c.gender == ProtoCrewMember.Gender.Male ? Local.Kerbal_Male : Local.Kerbal_Female), Local.Rescuemission_msg2));                   //We found xx  "He"/"She"'s still alive!"
                }
            }

            // gift resources
            if (detected)
            {
                var reslib = PartResourceLibrary.Instance.resourceDefinitions;
                var parts  = Lib.GetPartsRecursively(v.rootPart);

                // give the vessel some propellant usable on eva
                string monoprop_name   = Lib.EvaPropellantName();
                double monoprop_amount = Lib.EvaPropellantCapacity();
                foreach (var part in parts)
                {
                    if (part.CrewCapacity > 0 || part.FindModuleImplementing <KerbalEVA>() != null)
                    {
                        if (Lib.Capacity(part, monoprop_name) <= double.Epsilon)
                        {
                            Lib.AddResource(part, monoprop_name, 0.0, monoprop_amount);
                        }
                        break;
                    }
                }
                ResourceCache.Produce(v, monoprop_name, monoprop_amount, ResourceBroker.Generic);

                // give the vessel some supplies
                Profile.SetupRescue(v);
            }
        }
Пример #22
0
  // draw a vessel in the monitor
  // - return: 1 if vessel wasn't skipped
  uint render_vessel(Vessel v)
  {
    // get vessel info from cache
    vessel_info vi = Cache.VesselInfo(v);

    // skip invalid vessels
    if (!vi.is_valid) return 0;

    // get vessel data from the db
    vessel_data vd = DB.VesselData(v.id);

    // skip filtered vessels
    if (filtered() && vd.group != filter) return 0;

    // get vessel crew
    List<ProtoCrewMember> crew = v.loaded ? v.GetVesselCrew() : v.protoVessel.GetVesselCrew();

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

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

    // store problems icons & tooltips
    List<Texture> problem_icons = new List<Texture>();
    List<string> problem_tooltips = new List<string>();

    // detect problems
    problem_sunlight(vi, ref problem_icons, ref problem_tooltips);
    problem_storm(v, ref problem_icons, ref problem_tooltips);
    if (crew.Count > 0)
    {
      problem_kerbals(crew, ref problem_icons, ref problem_tooltips);
      problem_radiation(vi, ref problem_icons, ref problem_tooltips);
      problem_scrubbers(v, vi.scrubbers, ref problem_icons, ref problem_tooltips);
      problem_recyclers(v, vi.recyclers, ref problem_icons, ref problem_tooltips);
    }
    problem_greenhouses(v, vi.greenhouses, ref problem_icons, ref problem_tooltips);

    // choose problem icon
    const UInt64 problem_icon_time = 3;
    Texture problem_icon = icon_empty;
    if (problem_icons.Count > 0)
    {
      UInt64 problem_index = ((UInt64)Time.realtimeSinceStartup / problem_icon_time) % (UInt64)(problem_icons.Count);
      problem_icon = problem_icons[(int)problem_index];
    }

    // generate problem tooltips
    string problem_tooltip = String.Join("\n", problem_tooltips.ToArray());

    // render vessel name & icons
    GUILayout.BeginHorizontal(row_style);
    GUILayout.Label(new GUIContent(Lib.BuildString("<b>", Lib.Epsilon(vessel_name, 18), "</b>"), vessel_name.Length > 18 ? vessel_name : ""), name_style);
    GUILayout.Label(new GUIContent(Lib.Epsilon(body_name, 8), body_name.Length > 8 ? body_name : ""), body_style);
    GUILayout.Label(new GUIContent(problem_icon, problem_tooltip), icon_style);
    GUILayout.Label(indicator_ec(v), icon_style);
    if (Kerbalism.supply_rules.Count > 0) GUILayout.Label(indicator_supplies(v, vi), icon_style);
    if (Kerbalism.features.reliability) GUILayout.Label(indicator_reliability(v, vi), icon_style);
    if (Kerbalism.features.signal) GUILayout.Label(indicator_signal(v, vi), icon_style);
    GUILayout.EndHorizontal();
    if (Lib.IsClicked(1)) Info.Toggle(v);
    else if (Lib.IsClicked(2) && !v.isEVA) Console.Toggle(v);

    // remember last vessel clicked
    if (Lib.IsClicked()) last_clicked_id = v.id;

    // render vessel config
    if (configured_id == v.id) render_config(v);

    // spacing between vessels
    GUILayout.Space(10.0f);

    // signal that the vessel wasn't skipped for whatever reason
    return 1;
  }
Пример #23
0
 // return a verbose description of shielding capability
 public static string Shielding_to_string(double v)
 {
     return(v <= double.Epsilon ? Local.Habitat_none : Lib.BuildString((20.0 * v / PreferencesRadiation.Instance.shieldingEfficiency).ToString("F2"), " mm"));//"none"
 }
Пример #24
0
        public void Repair()
        {
            // disable for dead eva kerbals
            Vessel v = FlightGlobals.ActiveVessel;

            if (v == null || EVA.IsDead(v))
            {
                return;
            }

            // check trait
            if (!repair_cs.Check(v))
            {
                Message.Post
                (
                    Lib.TextVariant
                    (
                        "I'm not qualified for this",
                        "I will not even know where to start",
                        "I'm afraid I can't do that"
                    ),
                    repair_cs.Warning()
                );
                return;
            }

            // flag as not broken
            broken = false;

            // reset times
            last = 0.0;
            next = 0.0;

            // re-enable module
            foreach (PartModule m in modules)
            {
                m.isEnabled = true;
                m.enabled   = true;
            }

            // we need to reconfigure the module here, because if all modules of a type
            // share the broken state, and these modules are part of a configure setup,
            // then repairing will enable all of them, messing up with the configuration
            part.FindModulesImplementing <Configure>().ForEach(k => k.DoConfigure());

            // type-specific hacks
            Apply(false);

            // notify user
            Message.Post
            (
                Lib.BuildString("<b>", title, "</b> repaired"),
                Lib.TextVariant
                (
                    "A powerkick did the trick",
                    "Duct tape, is there something it can't fix?",
                    "Fully operational again",
                    "We are back in business"
                )
            );
        }
Пример #25
0
        bool Render_Vessel(Panel p, Vessel v)
        {
            // get vessel info
            Vessel_Info vi = Cache.VesselInfo(v);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            // done
            return(true);
        }
Пример #26
0
 // module info support
 public string GetModuleTitle()
 {
     return(Lib.BuildString(title, " Reliability"));
 }
Пример #27
0
        // TODO: Implement support to CommNet frequency
        void Indicator_Signal(Panel p, Vessel v, Vessel_Info vi)
        {
            ConnectionInfo conn = vi.connection;

            // target name
            string target_str = string.Empty;

            switch (vi.connection.status)
            {
            case LinkStatus.direct_link: target_str = "DSN"; break;

            case LinkStatus.indirect_link: target_str = vi.connection.path[vi.connection.path.Count - 1].vesselName; break;

            default: target_str = "none"; break;
            }

            // transmitted label, content and tooltip
            string comms_label   = vi.relaying.Length == 0 ? "transmitting" : "relaying";
            string comms_str     = vi.connection.linked ? "telemetry" : "nothing";
            string comms_tooltip = string.Empty;

            if (vi.relaying.Length > 0)
            {
                ExperimentInfo exp = Science.Experiment(vi.relaying);
                comms_str     = exp.name;
                comms_tooltip = exp.fullname;
            }
            else if (vi.transmitting.Length > 0)
            {
                ExperimentInfo exp = Science.Experiment(vi.transmitting);
                comms_str     = exp.name;
                comms_tooltip = exp.fullname;
            }

            string tooltip = Lib.BuildString
                             (
                "<align=left />",
                "connected\t<b>", vi.connection.linked ? "yes" : "no", "</b>\n",
                "rate\t\t<b>", Lib.HumanReadableDataRate(vi.connection.rate), "</b>\n",
                "target\t\t<b>", target_str, "</b>\n",
                comms_label, "\t<b>", comms_str, "</b>"
                             );

            Texture image = Icons.signal_red;

            switch (conn.status)
            {
            case LinkStatus.direct_link:
                image = vi.connection.rate > 0.005 ? Icons.signal_white : Icons.signal_yellow;
                break;

            case LinkStatus.indirect_link:
                image    = vi.connection.rate > 0.005 ? Icons.signal_white : Icons.signal_yellow;
                tooltip += "\n\n<color=yellow>Signal relayed</color>";
                break;

            case LinkStatus.no_link:
                image = Icons.signal_red;
                break;

            case LinkStatus.no_antenna:
                image    = Icons.signal_red;
                tooltip += "\n\n<color=red>No antenna</color>";
                break;

            case LinkStatus.blackout:
                image    = Icons.signal_red;
                tooltip += "\n\n<color=red>Blackout</color>";
                break;
            }

            p.SetIcon(image, tooltip);
        }
Пример #28
0
 public override string GetModuleDisplayName()
 {
     return(Lib.BuildString(title, " Reliability"));
 }
Пример #29
0
        public static void BackgroundUpdate(Vessel v, ProtoPartModuleSnapshot m, Greenhouse g,
                                            Vessel_Info vi, Vessel_Resources resources, double elapsed_s)
        {
            // get protomodule data
            bool   active = Lib.Proto.GetBool(m, "active");
            double growth = Lib.Proto.GetDouble(m, "growth");

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

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

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

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

                // execute recipe
                Resource_Recipe recipe = new Resource_Recipe();
                foreach (ModuleResource input in g.resHandler.inputResources)
                {
                    recipe.Input(input.name, input.rate * elapsed_s);
                }
                foreach (ModuleResource output in g.resHandler.outputResources)
                {
                    recipe.Output(output.name, output.rate * elapsed_s, true);
                }
                resources.Transform(recipe);

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

                // determine inputs conditions
                // note: comparing against amounts in previous simulation step
                bool   inputs      = true;
                string missing_res = string.Empty;
                foreach (ModuleResource input in g.resHandler.inputResources)
                {
                    if (resources.Info(v, input.name).amount <= double.Epsilon)
                    {
                        inputs      = false;
                        missing_res = input.name;
                        break;
                    }
                }

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

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

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

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

                // update protomodule data
                Lib.Proto.Set(m, "natural", natural);
                Lib.Proto.Set(m, "artificial", artificial);
                Lib.Proto.Set(m, "tta", tta);
                Lib.Proto.Set(m, "issue", issue);
                Lib.Proto.Set(m, "growth", growth);
            }
        }
Пример #30
0
        }                                                                                                                        // Display after config widget

        public override string GetModuleDisplayName()
        {
            return(Lib.BuildString("<size=1><color=#00000000>01</color></size>", title));
        }                                                                                                                                       // Display after config widget