public bool Add(Action<MonitorPage> function, MonitorPage page)
			{
				if (function == selector) {
					pages.Add(page);
					return true;
				}
				return false;
			}
Beispiel #2
0
 public bool Add(Action <MonitorPage> function, MonitorPage page)
 {
     if (function == selector)
     {
         pages.Add(page);
         return(true);
     }
     return(false);
 }
Beispiel #3
0
        /**
         * Render the text.  Assumes screen has already been cleared, so all we have to do here
         * is prepare the text objects and draw the text.
         */
        public void Render(RenderTexture screen, string screenText, MonitorPage activePage)
        {
            bool textDirty = (cachedText != screenText);

            if (textDirty == false && cachedOverlayText != activePage.textOverlay)
            {
                textDirty = true;
            }

            if (textDirty)
            {
                //JUtil.LogMessage(this, "Render: textDirty, so regenerating everything");
                cachedText        = screenText;
                cachedOverlayText = activePage.textOverlay;

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Clear();
                }

                if (!string.IsNullOrEmpty(cachedText))
                {
                    string[] textLines = cachedText.Split(JUtil.LineSeparator, StringSplitOptions.None);
                    ParseText(textLines, activePage.screenXMin, activePage.screenYMin, activePage.defaultColor, activePage.pageFont);
                }

                if (!string.IsNullOrEmpty(cachedOverlayText))
                {
                    string[] overlayLines = cachedOverlayText.Split(JUtil.LineSeparator, StringSplitOptions.None);
                    ParseText(overlayLines, 0, 0, activePage.defaultColor, activePage.pageFont);
                }

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Bake();
                }
            }

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(true, fontRenderer[i].obj);
                }
            }

            textCamera.targetTexture = screen;
            textCamera.Render();

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(false, fontRenderer[i].obj);
                }
            }
        }
Beispiel #4
0
        /**
         * Render the text.  Assumes screen has already been cleared, so all we have to do here
         * is prepare the text objects and draw the text.
         */
        public void Render(RenderTexture screen, MonitorPage activePage)
        {
            int  screenTextHash  = activePage.Text.GetHashCode();
            int  overlayTextHash = activePage.textOverlay.GetHashCode();
            bool textDirty       = (cachedTextHash != screenTextHash) || (cachedOverlayTextHash != overlayTextHash);

            if (textDirty)
            {
                cachedTextHash        = screenTextHash;
                cachedOverlayTextHash = overlayTextHash;

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Clear();
                }

                if (!string.IsNullOrEmpty(activePage.Text))
                {
                    ParseText(activePage.Text, activePage.screenXMin, activePage.screenYMin, activePage.defaultColor, activePage.pageFont);
                }

                if (!string.IsNullOrEmpty(activePage.textOverlay))
                {
                    ParseText(activePage.textOverlay, 0, 0, activePage.defaultColor, activePage.pageFont);
                }

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Bake();
                }
            }

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(true, fontRenderer[i].obj);
                }
            }

            textCamera.targetTexture = screen;
            textCamera.Render();

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(false, fontRenderer[i].obj);
                }
            }
        }
        public static void CreateButton(InternalProp thatProp, string buttonName, MonitorPage thatPage, Action<MonitorPage> handlerFunction, InternalModel thatModel = null)
        {
            SmarterButton buttonBehaviour;
            if ((buttonBehaviour = AttachBehaviour(thatProp, thatModel, buttonName)) == null)
            {
                return;
            }
            foreach (PageTriggerSet pageset in buttonBehaviour.pageTriggers)
            {
                if (pageset.Add(handlerFunction, thatPage))
                {
                    return;
                }
            }

            buttonBehaviour.pageTriggers.Add(new PageTriggerSet(handlerFunction, thatPage));
            buttonBehaviour.part = (thatModel == null) ? thatProp.part : thatModel.part;
        }
Beispiel #6
0
        public static void CreateButton(InternalProp thatProp, string buttonName, MonitorPage thatPage, Action <MonitorPage> handlerFunction, InternalModel thatModel = null)
        {
            SmarterButton buttonBehaviour;

            if ((buttonBehaviour = AttachBehaviour(thatProp, thatModel, buttonName)) == null)
            {
                return;
            }
            foreach (PageTriggerSet pageset in buttonBehaviour.pageTriggers)
            {
                if (pageset.Add(handlerFunction, thatPage))
                {
                    return;
                }
            }

            buttonBehaviour.pageTriggers.Add(new PageTriggerSet(handlerFunction, thatPage));
        }
        public void PageButtonClick(MonitorPage triggeredPage)
        {
            if (needsElectricCharge && electricChargeReserve < 0.01d)
            {
                return;
            }

            // Apply page redirect like this:
            triggeredPage = FindPageByName(activePage.ContextRedirect(triggeredPage.name)) ?? triggeredPage;
            if (triggeredPage != activePage && (activePage.SwitchingPermitted(triggeredPage.name) || triggeredPage.unlocker))
            {
                activePage.Active(false);
                activePage = triggeredPage;
                activePage.Active(true);
                persistence.SetVar(persistentVarName, activePage.pageNumber);
                refreshDrawCountdown = refreshTextCountdown = 0;
                firstRenderComplete  = false;
                PlayClickSound(audioOutput);
            }
        }
        public void PageButtonClick(MonitorPage triggeredPage)
        {
            if (resourceDepleted || noCommConnection)
            {
                return;
            }

            // Apply page redirect like this:
            triggeredPage = FindPageByName(activePage.ContextRedirect(triggeredPage.name)) ?? triggeredPage;
            if (triggeredPage != activePage && (activePage.SwitchingPermitted(triggeredPage.name) || triggeredPage.unlocker))
            {
                activePage.Active(false);
                activePage = triggeredPage;
                activePage.Active(true);
                rpmComp.SetPersistentVariable(persistentVarName, activePage.pageNumber, false);
                refreshDrawCountdown = refreshTextCountdown = 0;
                firstRenderComplete  = false;
                PlayClickSound(audioOutput);
            }
        }
        /**
         * Render the text.  Assumes screen has already been cleared, so all we have to do here
         * is prepare the text objects and draw the text.
         */
        public void Render(RenderTexture screen, string screenText, MonitorPage activePage)
        {
            bool textDirty = (cachedText != screenText);

            if (textDirty == false && cachedOverlayText != activePage.textOverlay)
            {
                textDirty = true;
            }

            if (textDirty)
            {
                //JUtil.LogMessage(this, "Render: textDirty, so regenerating everything");
                cachedText = screenText;
                cachedOverlayText = activePage.textOverlay;

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Clear();
                }

                if (!string.IsNullOrEmpty(cachedText))
                {
                    string[] textLines = cachedText.Split(JUtil.LineSeparator, StringSplitOptions.None);
                    ParseText(textLines, activePage.screenXMin, activePage.screenYMin, activePage.defaultColor, activePage.pageFont);
                }

                if (!string.IsNullOrEmpty(cachedOverlayText))
                {
                    string[] overlayLines = cachedOverlayText.Split(JUtil.LineSeparator, StringSplitOptions.None);
                    ParseText(overlayLines, 0, 0, activePage.defaultColor, activePage.pageFont);
                }

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Bake();
                }
            }

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(true, fontRenderer[i].obj);
                }
            }

            textCamera.targetTexture = screen;
            textCamera.Render();

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(false, fontRenderer[i].obj);
                }
            }
        }
        /**
         * Render the text.  Assumes screen has already been cleared, so all we have to do here
         * is prepare the text objects and draw the text.
         */
        public void Render(RenderTexture screen, MonitorPage activePage)
        {
            int screenTextHash = activePage.Text.GetHashCode();
            int overlayTextHash = activePage.textOverlay.GetHashCode();
            bool textDirty = (cachedTextHash != screenTextHash) || (cachedOverlayTextHash != overlayTextHash);

            if (textDirty)
            {
                cachedTextHash = screenTextHash;
                cachedOverlayTextHash = overlayTextHash;

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Clear();
                }

                if (!string.IsNullOrEmpty(activePage.Text))
                {
                    ParseText(activePage.Text, activePage.screenXMin, activePage.screenYMin, activePage.defaultColor, activePage.pageFont);
                }

                if (!string.IsNullOrEmpty(activePage.textOverlay))
                {
                    ParseText(activePage.textOverlay, 0, 0, activePage.defaultColor, activePage.pageFont);
                }

                for (int i = 0; i < fontRenderer.Count; ++i)
                {
                    fontRenderer[i].Bake();
                }
            }

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(true, fontRenderer[i].obj);
                }
            }

            textCamera.targetTexture = screen;
            textCamera.Render();

            for (int i = 0; i < fontRenderer.Count; ++i)
            {
                if (fontRenderer[i].mesh.vertexCount > 0)
                {
                    JUtil.ShowHide(false, fontRenderer[i].obj);
                }
            }
        }
Beispiel #11
0
 public PageTriggerSet(Action <MonitorPage> function, MonitorPage page)
 {
     selector = function;
     pages.Add(page);
 }
		public void Start()
		{

			// If we're not in the correct location, there's no point doing anything.
			if (!InstallationPathWarning.Warn())
				return;

			try {

				// Install the calculator module.
				comp = RasterPropMonitorComputer.Instantiate(internalProp);
				comp.UpdateRefreshRates(refreshTextRate, refreshDataRate);

				// Loading the font...
				fontTexture.Add(LoadFont(this, internalProp, fontTransform, false));

				// Damn KSP's config parser!!!
				if (!string.IsNullOrEmpty(emptyColor))
					emptyColorValue = ConfigNode.ParseColor32(emptyColor);
				if (!string.IsNullOrEmpty(defaultFontTint))
					defaultFontTintValue = ConfigNode.ParseColor32(defaultFontTint);

				if (!string.IsNullOrEmpty(fontDefinition)) {
					JUtil.LogMessage(this, "Loading font definition from {0}", fontDefinition);
					fontDefinitionString = File.ReadAllLines(KSPUtil.ApplicationRootPath + "GameData/" + fontDefinition.EnforceSlashes(), Encoding.UTF8)[0];
				}

				// We can pre-compute the rectangles the font characters will be copied from, this seems to make it slightly quicker...
				// although I'm not sure I'm not seeing things by this point.
				int fontLettersX = (fontTexture[0].width / fontLetterWidth);
				int fontLettersY = (fontTexture[0].height / fontLetterHeight);
				float letterSpanX = 1f / fontLettersX;
				float letterSpanY = 1f / fontLettersY;
				int lastCharacter = fontLettersX * fontLettersY;

				if (lastCharacter != fontDefinitionString.Length) {
					JUtil.LogMessage(this, "Warning, number of letters in the font definition does not match font bitmap size.");
				}

				for (int i = 0; i < lastCharacter && i < fontDefinitionString.Length; i++) {
					int xSource = i % fontLettersX;
					int ySource = (i - xSource) / fontLettersX;
					if (!fontCharacters.ContainsKey(fontDefinitionString[i]))
						fontCharacters[fontDefinitionString[i]] = new Rect(letterSpanX * xSource, letterSpanY * (fontLettersY - ySource - 1), letterSpanX, letterSpanY);
				}

				// And a little optimisation for superscript/subscript:
				fontLetterHalfHeight = fontLetterHeight / 2f;
				fontLetterHalfWidth = fontLetterWidth / 2f;
				fontLetterDoubleWidth = fontLetterWidth * 2f;

				// Now that is done, proceed to setting up the screen.

				screenTexture = new RenderTexture(screenPixelWidth, screenPixelHeight, 24, RenderTextureFormat.ARGB32);
				screenMat = internalProp.FindModelTransform(screenTransform).renderer.material;
				foreach (string layerID in textureLayerID.Split())
					screenMat.SetTexture(layerID.Trim(), screenTexture);

				if (GameDatabase.Instance.ExistsTexture(noSignalTextureURL.EnforceSlashes())) {
					noSignalTexture = GameDatabase.Instance.GetTexture(noSignalTextureURL.EnforceSlashes(), false);
				}

				// Create camera instance...
				cameraStructure = new FlyingCamera(part, screenTexture, cameraAspect);

				// The neat trick. IConfigNode doesn't work. No amount of kicking got it to work.
				// Well, we don't need it. GameDatabase, gimme config nodes for all props!
				foreach (ConfigNode node in GameDatabase.Instance.GetConfigNodes ("PROP")) {
					// Now, we know our own prop name.
					if (node.GetValue("name") == internalProp.propName) {
						// So this is the configuration of our prop in memory. Nice place.
						// We know it contains at least one MODULE node, us.
						// And we know our moduleID, which is the number in order of being listed in the prop.
						// Therefore the module by that number is our module's own config node.

						ConfigNode moduleConfig = node.GetNodes("MODULE")[moduleID];
						ConfigNode[] pageNodes = moduleConfig.GetNodes("PAGE");

						// Which we can now parse for page definitions.
						for (int i = 0; i < pageNodes.Length; i++) {
							// Mwahahaha.
							try {
								var newPage = new MonitorPage(i, pageNodes[i], this);
								activePage = activePage ?? newPage;
								if (newPage.isDefault)
									activePage = newPage;
								pages.Add(newPage);
							} catch (ArgumentException e) {
								JUtil.LogMessage(this, "Warning - {0}", e);
							}
							
						}

						// Now that all pages are loaded, we can use the moment in the loop to suck in all the extra fonts.
						foreach (string value in moduleConfig.GetValues("extraFont")) {
							fontTexture.Add(LoadFont(this, internalProp, value, true));
						}

						break;
					}
				}
				JUtil.LogMessage(this, "Done setting up pages, {0} pages ready.", pages.Count);

				// Load our state from storage...
				persistentVarName = "activePage" + internalProp.propID;
				persistence = new PersistenceAccessor(part);
				int? activePageID = persistence.GetVar(persistentVarName);
				if (activePageID != null && activePageID.Value < pages.Count) {
					activePage = pages[activePageID.Value];
				}
				activePage.Active(true);

				// If we have global buttons, set them up.
				if (!string.IsNullOrEmpty(globalButtons)) {
					string[] tokens = globalButtons.Split(',');
					for (int i = 0; i < tokens.Length; i++) {
						string buttonName = tokens[i].Trim();
						// Notice that holes in the global button list ARE legal.
						if (!string.IsNullOrEmpty(buttonName))
							SmarterButton.CreateButton(internalProp, buttonName, i, GlobalButtonClick, GlobalButtonRelease);
					}
				}

				audioOutput = JUtil.SetupIVASound(internalProp, buttonClickSound, buttonClickVolume, false);

				// One last thing to make sure of: If our pod is transparent, we're always active.
				ourPodIsTransparent = JUtil.IsPodTransparent(part);

				// And if the try block never completed, startupComplete will never be true.
				startupComplete = true;
			} catch {
				JUtil.AnnoyUser(this);
				startupFailed = true;
				// We can also disable ourselves, that should help.
				enabled = false;
				// And now that we notified the user that config is borked, we rethrow the exception so that
				// it gets logged and we can debug.
				throw;
			}

		}
        public void Start()
        {
            // If we're not in the correct location, there's no point doing anything.
            if (!InstallationPathWarning.Warn())
            {
                return;
            }

            if (HighLogic.LoadedSceneIsEditor)
            {
                return;
            }

            try
            {
                rpmComp = RasterPropMonitorComputer.Instantiate(internalProp, true);
                JUtil.LogMessage(this, "Attaching monitor {2}-{1} to {0}", rpmComp.RPMCid, internalProp.propID, internalProp.internalModel.internalName);

                // Install the calculator module.
                rpmComp.UpdateDataRefreshRate(refreshDataRate);

                // Loading the font...
                List <Texture2D> fontTexture = new List <Texture2D>();
                fontTexture.Add(LoadFont(this, internalProp, fontTransform));

                // Damn KSP's config parser!!!
                if (!string.IsNullOrEmpty(emptyColor))
                {
                    emptyColorValue = ConfigNode.ParseColor32(emptyColor);
                }
                if (!string.IsNullOrEmpty(defaultFontTint))
                {
                    defaultFontTintValue = ConfigNode.ParseColor32(defaultFontTint);
                }

                if (!string.IsNullOrEmpty(fontDefinition))
                {
                    JUtil.LogMessage(this, "Loading font definition from {0}", fontDefinition);
                    fontDefinitionString = File.ReadAllLines(KSPUtil.ApplicationRootPath + "GameData/" + fontDefinition.EnforceSlashes(), Encoding.UTF8)[0];
                }

                // Now that is done, proceed to setting up the screen.

                screenTexture = new RenderTexture(screenPixelWidth, screenPixelHeight, 24, RenderTextureFormat.ARGB32);
                screenMat     = internalProp.FindModelTransform(screenTransform).GetComponent <Renderer>().material;

                bool manuallyInvertY = false;
                if (SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 9") || SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 11") || SystemInfo.graphicsDeviceVersion.StartsWith("Direct3D 12"))
                {
                    manuallyInvertY = (UnityEngine.QualitySettings.antiAliasing > 0);
                }

                foreach (string layerID in textureLayerID.Split())
                {
                    screenMat.SetTexture(layerID.Trim(), screenTexture);
                    // This code was written for a much older flavor of Unity, and the Unity 2017.1 update broke
                    // some assumptions about who managed the y-inversion issue between OpenGL and DX9.
                    if (manuallyInvertY)
                    {
                        screenMat.SetTextureScale(layerID.Trim(), new Vector2(1.0f, -1.0f));
                        screenMat.SetTextureOffset(layerID.Trim(), new Vector2(0.0f, 1.0f));
                    }
                }

                if (GameDatabase.Instance.ExistsTexture(noSignalTextureURL.EnforceSlashes()))
                {
                    noSignalTexture = GameDatabase.Instance.GetTexture(noSignalTextureURL.EnforceSlashes(), false);
                }

                // The neat trick. IConfigNode doesn't work. No amount of kicking got it to work.
                // Well, we don't need it. GameDatabase, gimme config nodes for all props!
                foreach (ConfigNode node in GameDatabase.Instance.GetConfigNodes("PROP"))
                {
                    // Now, we know our own prop name.
                    if (node.GetValue("name") == internalProp.propName)
                    {
                        // So this is the configuration of our prop in memory. Nice place.
                        // We know it contains at least one MODULE node, us.
                        // And we know our moduleID, which is the number in order of being listed in the prop.
                        // Therefore the module by that number is our module's own config node.

                        ConfigNode   moduleConfig = node.GetNodes("MODULE")[moduleID];
                        ConfigNode[] pageNodes    = moduleConfig.GetNodes("PAGE");

                        // Which we can now parse for page definitions.
                        for (int i = 0; i < pageNodes.Length; i++)
                        {
                            // Mwahahaha.
                            try
                            {
                                var newPage = new MonitorPage(i, pageNodes[i], this);
                                activePage = activePage ?? newPage;
                                if (newPage.isDefault)
                                {
                                    activePage = newPage;
                                }
                                pages.Add(newPage);
                            }
                            catch (ArgumentException e)
                            {
                                JUtil.LogMessage(this, "Warning - {0}", e);
                            }
                        }

                        // Now that all pages are loaded, we can use the moment in the loop to suck in all the extra fonts.
                        foreach (string value in moduleConfig.GetValues("extraFont"))
                        {
                            fontTexture.Add(LoadFont(this, internalProp, value));
                        }

                        break;
                    }
                }

                JUtil.LogMessage(this, "Done setting up pages, {0} pages ready.", pages.Count);

                textRenderer = new TextRenderer(fontTexture, new Vector2((float)fontLetterWidth, (float)fontLetterHeight), fontDefinitionString, 17, screenPixelWidth, screenPixelHeight);

                // Load our state from storage...
                persistentVarName = "activePage" + internalProp.propID;
                int activePageID = rpmComp.GetPersistentVariable(persistentVarName, pages.Count, false).MassageToInt();
                if (activePageID < pages.Count)
                {
                    activePage = pages[activePageID];
                }
                activePage.Active(true);

                // If we have global buttons, set them up.
                if (!string.IsNullOrEmpty(globalButtons))
                {
                    string[] tokens = globalButtons.Split(',');
                    for (int i = 0; i < tokens.Length; i++)
                    {
                        string buttonName = tokens[i].Trim();
                        // Notice that holes in the global button list ARE legal.
                        if (!string.IsNullOrEmpty(buttonName))
                        {
                            SmarterButton.CreateButton(internalProp, buttonName, i, GlobalButtonClick, GlobalButtonRelease);
                        }
                    }
                }

                audioOutput = JUtil.SetupIVASound(internalProp, buttonClickSound, buttonClickVolume, false);

                if (needsElectricCharge)
                {
                    delResourceCallback = (Action <bool>)Delegate.CreateDelegate(typeof(Action <bool>), this, "ResourceDepletedCallback");
                    rpmComp.RegisterResourceCallback(resourceName, delResourceCallback);
                }

                if (needsCommConnection)
                {
                    delCommConnectionCallback = (Action <float>)Delegate.CreateDelegate(typeof(Action <float>), this, "CommConnectionCallback");
                    rpmComp.RegisterVariableCallback("COMMNETVESSELCONTROLSTATE", delCommConnectionCallback);
                }

                // And if the try block never completed, startupComplete will never be true.
                startupComplete = true;
            }
            catch
            {
                JUtil.AnnoyUser(this);
                // We can also disable ourselves, that should help.
                enabled = false;
                // And now that we notified the user that config is borked, we rethrow the exception so that
                // it gets logged and we can debug.
                throw;
            }
        }
		public void PageButtonClick(MonitorPage triggeredPage)
		{
			if (needsElectricCharge && electricChargeReserve < 0.01d)
				return;
			// Apply page redirect like this:
			triggeredPage = FindPageByName(activePage.ContextRedirect(triggeredPage.name)) ?? triggeredPage;
			if (triggeredPage != activePage && (activePage.SwitchingPermitted(triggeredPage.name) || triggeredPage.unlocker)) {
				activePage.Active(false);
				activePage = triggeredPage;
				activePage.Active(true);
				persistence.SetVar(persistentVarName, activePage.pageNumber);
				refreshDrawCountdown = refreshTextCountdown = 0;
				comp.updateForced = true;
				firstRenderComplete = false;
				PlayClickSound(audioOutput);
			}
		}
        public void PageButtonClick(MonitorPage triggeredPage)
        {
            if (resourceDepleted)
            {
                return;
            }

            // Apply page redirect like this:
            triggeredPage = FindPageByName(activePage.ContextRedirect(triggeredPage.name)) ?? triggeredPage;
            if (triggeredPage != activePage && (activePage.SwitchingPermitted(triggeredPage.name) || triggeredPage.unlocker))
            {
                activePage.Active(false);
                activePage = triggeredPage;
                activePage.Active(true);
                rpmComp.SetPersistentVariable(persistentVarName, activePage.pageNumber, false);
                refreshDrawCountdown = refreshTextCountdown = 0;
                firstRenderComplete = false;
                PlayClickSound(audioOutput);
            }
        }
        public void Start()
        {
            // If we're not in the correct location, there's no point doing anything.
            if (!InstallationPathWarning.Warn())
            {
                return;
            }

            if (HighLogic.LoadedSceneIsEditor)
            {
                return;
            }

            try
            {
                // Install the calculator module.
                RPMVesselComputer comp = RPMVesselComputer.Instance(vessel);
                comp.UpdateDataRefreshRate(refreshDataRate);

                persistence = new PersistenceAccessor(internalProp);

                // Loading the font...
                List <Texture2D> fontTexture = new List <Texture2D>();
                fontTexture.Add(LoadFont(this, internalProp, fontTransform));

                // Damn KSP's config parser!!!
                if (!string.IsNullOrEmpty(emptyColor))
                {
                    emptyColorValue = ConfigNode.ParseColor32(emptyColor);
                }
                if (!string.IsNullOrEmpty(defaultFontTint))
                {
                    defaultFontTintValue = ConfigNode.ParseColor32(defaultFontTint);
                }

                if (!string.IsNullOrEmpty(fontDefinition))
                {
                    JUtil.LogMessage(this, "Loading font definition from {0}", fontDefinition);
                    fontDefinitionString = File.ReadAllLines(KSPUtil.ApplicationRootPath + "GameData/" + fontDefinition.EnforceSlashes(), Encoding.UTF8)[0];
                }

                // Now that is done, proceed to setting up the screen.

                screenTexture = new RenderTexture(screenPixelWidth, screenPixelHeight, 24, RenderTextureFormat.ARGB32);
                screenMat     = internalProp.FindModelTransform(screenTransform).renderer.material;
                foreach (string layerID in textureLayerID.Split())
                {
                    screenMat.SetTexture(layerID.Trim(), screenTexture);
                }

                if (GameDatabase.Instance.ExistsTexture(noSignalTextureURL.EnforceSlashes()))
                {
                    noSignalTexture = GameDatabase.Instance.GetTexture(noSignalTextureURL.EnforceSlashes(), false);
                }

                // Create camera instance...
                cameraStructure = new FlyingCamera(part, screenTexture, cameraAspect);

                // The neat trick. IConfigNode doesn't work. No amount of kicking got it to work.
                // Well, we don't need it. GameDatabase, gimme config nodes for all props!
                foreach (ConfigNode node in GameDatabase.Instance.GetConfigNodes("PROP"))
                {
                    // Now, we know our own prop name.
                    if (node.GetValue("name") == internalProp.propName)
                    {
                        // So this is the configuration of our prop in memory. Nice place.
                        // We know it contains at least one MODULE node, us.
                        // And we know our moduleID, which is the number in order of being listed in the prop.
                        // Therefore the module by that number is our module's own config node.

                        ConfigNode   moduleConfig = node.GetNodes("MODULE")[moduleID];
                        ConfigNode[] pageNodes    = moduleConfig.GetNodes("PAGE");

                        // Which we can now parse for page definitions.
                        for (int i = 0; i < pageNodes.Length; i++)
                        {
                            // Mwahahaha.
                            try
                            {
                                var newPage = new MonitorPage(i, pageNodes[i], this);
                                activePage = activePage ?? newPage;
                                if (newPage.isDefault)
                                {
                                    activePage = newPage;
                                }
                                pages.Add(newPage);
                            }
                            catch (ArgumentException e)
                            {
                                JUtil.LogMessage(this, "Warning - {0}", e);
                            }
                        }

                        // Now that all pages are loaded, we can use the moment in the loop to suck in all the extra fonts.
                        foreach (string value in moduleConfig.GetValues("extraFont"))
                        {
                            fontTexture.Add(LoadFont(this, internalProp, value));
                        }

                        break;
                    }
                }
                JUtil.LogMessage(this, "Done setting up pages, {0} pages ready.", pages.Count);

                textRenderer = new TextRenderer(fontTexture, new Vector2((float)fontLetterWidth, (float)fontLetterHeight), fontDefinitionString, 17, screenPixelWidth, screenPixelHeight);

                // Load our state from storage...
                persistentVarName = "activePage" + internalProp.propID;
                int activePageID = persistence.GetVar(persistentVarName, pages.Count);
                if (activePageID < pages.Count)
                {
                    activePage = pages[activePageID];
                }
                activePage.Active(true);

                // If we have global buttons, set them up.
                if (!string.IsNullOrEmpty(globalButtons))
                {
                    string[] tokens = globalButtons.Split(',');
                    for (int i = 0; i < tokens.Length; i++)
                    {
                        string buttonName = tokens[i].Trim();
                        // Notice that holes in the global button list ARE legal.
                        if (!string.IsNullOrEmpty(buttonName))
                        {
                            SmarterButton.CreateButton(internalProp, buttonName, i, GlobalButtonClick, GlobalButtonRelease);
                        }
                    }
                }

                audioOutput = JUtil.SetupIVASound(internalProp, buttonClickSound, buttonClickVolume, false);

                // One last thing to make sure of: If our pod is transparent, we're always active.
                ourPodIsTransparent = JUtil.IsPodTransparent(part);

                // And if the try block never completed, startupComplete will never be true.
                startupComplete = true;
            }
            catch
            {
                JUtil.AnnoyUser(this);
                startupFailed = true;
                // We can also disable ourselves, that should help.
                enabled = false;
                // And now that we notified the user that config is borked, we rethrow the exception so that
                // it gets logged and we can debug.
                throw;
            }
        }
        public void Start()
        {
            // If we're not in the correct location, there's no point doing anything.
            if (!InstallationPathWarning.Warn())
            {
                return;
            }

            if (HighLogic.LoadedSceneIsEditor)
            {
                return;
            }

            try
            {
                rpmComp = RasterPropMonitorComputer.Instantiate(internalProp, true);
                JUtil.LogMessage(this, "Attaching monitor {2}-{1} to {0}", rpmComp.RPMCid, internalProp.propID, internalProp.internalModel.internalName);

                // Install the calculator module.
                rpmComp.UpdateDataRefreshRate(refreshDataRate);

                // Loading the font...
                List<Texture2D> fontTexture = new List<Texture2D>();
                fontTexture.Add(LoadFont(this, internalProp, fontTransform));

                // Damn KSP's config parser!!!
                if (!string.IsNullOrEmpty(emptyColor))
                {
                    emptyColorValue = ConfigNode.ParseColor32(emptyColor);
                }
                if (!string.IsNullOrEmpty(defaultFontTint))
                {
                    defaultFontTintValue = ConfigNode.ParseColor32(defaultFontTint);
                }

                if (!string.IsNullOrEmpty(fontDefinition))
                {
                    JUtil.LogMessage(this, "Loading font definition from {0}", fontDefinition);
                    fontDefinitionString = File.ReadAllLines(KSPUtil.ApplicationRootPath + "GameData/" + fontDefinition.EnforceSlashes(), Encoding.UTF8)[0];
                }

                // Now that is done, proceed to setting up the screen.

                screenTexture = new RenderTexture(screenPixelWidth, screenPixelHeight, 24, RenderTextureFormat.ARGB32);
                screenMat = internalProp.FindModelTransform(screenTransform).GetComponent<Renderer>().material;
                foreach (string layerID in textureLayerID.Split())
                {
                    screenMat.SetTexture(layerID.Trim(), screenTexture);
                }

                if (GameDatabase.Instance.ExistsTexture(noSignalTextureURL.EnforceSlashes()))
                {
                    noSignalTexture = GameDatabase.Instance.GetTexture(noSignalTextureURL.EnforceSlashes(), false);
                }

                // Create camera instance...
                cameraStructure = new FlyingCamera(part, cameraAspect);

                // The neat trick. IConfigNode doesn't work. No amount of kicking got it to work.
                // Well, we don't need it. GameDatabase, gimme config nodes for all props!
                foreach (ConfigNode node in GameDatabase.Instance.GetConfigNodes("PROP"))
                {
                    // Now, we know our own prop name.
                    if (node.GetValue("name") == internalProp.propName)
                    {
                        // So this is the configuration of our prop in memory. Nice place.
                        // We know it contains at least one MODULE node, us.
                        // And we know our moduleID, which is the number in order of being listed in the prop.
                        // Therefore the module by that number is our module's own config node.

                        ConfigNode moduleConfig = node.GetNodes("MODULE")[moduleID];
                        ConfigNode[] pageNodes = moduleConfig.GetNodes("PAGE");

                        // Which we can now parse for page definitions.
                        for (int i = 0; i < pageNodes.Length; i++)
                        {
                            // Mwahahaha.
                            try
                            {
                                var newPage = new MonitorPage(i, pageNodes[i], this);
                                activePage = activePage ?? newPage;
                                if (newPage.isDefault)
                                    activePage = newPage;
                                pages.Add(newPage);
                            }
                            catch (ArgumentException e)
                            {
                                JUtil.LogMessage(this, "Warning - {0}", e);
                            }

                        }

                        // Now that all pages are loaded, we can use the moment in the loop to suck in all the extra fonts.
                        foreach (string value in moduleConfig.GetValues("extraFont"))
                        {
                            fontTexture.Add(LoadFont(this, internalProp, value));
                        }

                        break;
                    }
                }

                JUtil.LogMessage(this, "Done setting up pages, {0} pages ready.", pages.Count);

                textRenderer = new TextRenderer(fontTexture, new Vector2((float)fontLetterWidth, (float)fontLetterHeight), fontDefinitionString, 17, screenPixelWidth, screenPixelHeight);

                // Load our state from storage...
                persistentVarName = "activePage" + internalProp.propID;
                int activePageID = rpmComp.GetPersistentVariable(persistentVarName, pages.Count, false).MassageToInt();
                if (activePageID < pages.Count)
                {
                    activePage = pages[activePageID];
                }
                activePage.Active(true);

                // If we have global buttons, set them up.
                if (!string.IsNullOrEmpty(globalButtons))
                {
                    string[] tokens = globalButtons.Split(',');
                    for (int i = 0; i < tokens.Length; i++)
                    {
                        string buttonName = tokens[i].Trim();
                        // Notice that holes in the global button list ARE legal.
                        if (!string.IsNullOrEmpty(buttonName))
                            SmarterButton.CreateButton(internalProp, buttonName, i, GlobalButtonClick, GlobalButtonRelease);
                    }
                }

                audioOutput = JUtil.SetupIVASound(internalProp, buttonClickSound, buttonClickVolume, false);

                if (needsElectricCharge)
                {
                    del = (Action<bool>)Delegate.CreateDelegate(typeof(Action<bool>), this, "ResourceDepletedCallback");
                    rpmComp.RegisterResourceCallback(resourceName, del);
                }

                // And if the try block never completed, startupComplete will never be true.
                startupComplete = true;
            }
            catch
            {
                JUtil.AnnoyUser(this);
                // We can also disable ourselves, that should help.
                enabled = false;
                // And now that we notified the user that config is borked, we rethrow the exception so that
                // it gets logged and we can debug.
                throw;
            }
        }
 public PageTriggerSet(Action<MonitorPage> function, MonitorPage page)
 {
     selector = function;
     pages.Add(page);
     counter = -1;
 }
        public void Start()
        {
            InstallationPathWarning.Warn();

            // Install the calculator module.
            comp = RasterPropMonitorComputer.Instantiate(internalProp);
            comp.UpdateRefreshRates(refreshTextRate, refreshDataRate);

            // Loading the font...
            fontTexture.Add(LoadFont(this, internalProp, fontTransform, false));

            // Damn KSP's config parser!!!
            if (!string.IsNullOrEmpty(emptyColor))
            {
                emptyColorValue = ConfigNode.ParseColor32(emptyColor);
            }
            if (!string.IsNullOrEmpty(defaultFontTint))
            {
                defaultFontTintValue = ConfigNode.ParseColor32(defaultFontTint);
            }

            if (!string.IsNullOrEmpty(fontDefinition))
            {
                JUtil.LogMessage(this, "Loading font definition from {0}", fontDefinition);
                fontDefinitionString = File.ReadAllLines(KSPUtil.ApplicationRootPath + "GameData/" + fontDefinition.EnforceSlashes(), Encoding.UTF8)[0];
            }

            // We can pre-compute the rectangles the font characters will be copied from, this seems to make it slightly quicker...
            // although I'm not sure I'm not seeing things by this point.
            int   fontLettersX  = (fontTexture[0].width / fontLetterWidth);
            int   fontLettersY  = (fontTexture[0].height / fontLetterHeight);
            float letterSpanX   = 1f / fontLettersX;
            float letterSpanY   = 1f / fontLettersY;
            int   lastCharacter = fontLettersX * fontLettersY;

            if (lastCharacter != fontDefinitionString.Length)
            {
                JUtil.LogMessage(this, "Warning, number of letters in the font definition does not match font bitmap size.");
            }

            for (int i = 0; i < lastCharacter && i < fontDefinitionString.Length; i++)
            {
                int xSource = i % fontLettersX;
                int ySource = (i - xSource) / fontLettersX;
                if (!fontCharacters.ContainsKey(fontDefinitionString[i]))
                {
                    fontCharacters[fontDefinitionString[i]] = new Rect(letterSpanX * xSource, letterSpanY * (fontLettersY - ySource - 1), letterSpanX, letterSpanY);
                }
            }

            // And a little optimisation for superscript/subscript:
            fontLetterHalfHeight  = fontLetterHeight / 2f;
            fontLetterHalfWidth   = fontLetterWidth / 2f;
            fontLetterDoubleWidth = fontLetterWidth * 2f;

            // Now that is done, proceed to setting up the screen.

            screenTexture = new RenderTexture(screenPixelWidth, screenPixelHeight, 24, RenderTextureFormat.ARGB32);
            screenMat     = internalProp.FindModelTransform(screenTransform).renderer.material;
            foreach (string layerID in textureLayerID.Split())
            {
                screenMat.SetTexture(layerID.Trim(), screenTexture);
            }

            if (GameDatabase.Instance.ExistsTexture(noSignalTextureURL.EnforceSlashes()))
            {
                noSignalTexture = GameDatabase.Instance.GetTexture(noSignalTextureURL.EnforceSlashes(), false);
            }

            // Create camera instance...
            cameraStructure = new FlyingCamera(part, screenTexture, cameraAspect);

            // The neat trick. IConfigNode doesn't work. No amount of kicking got it to work.
            // Well, we don't need it. GameDatabase, gimme config nodes for all props!
            foreach (ConfigNode node in GameDatabase.Instance.GetConfigNodes("PROP"))
            {
                // Now, we know our own prop name.
                if (node.GetValue("name") == internalProp.propName)
                {
                    // So this is the configuration of our prop in memory. Nice place.
                    // We know it contains at least one MODULE node, us.
                    // And we know our moduleID, which is the number in order of being listed in the prop.
                    // Therefore the module by that number is our module's own config node.

                    ConfigNode   moduleConfig = node.GetNodes("MODULE")[moduleID];
                    ConfigNode[] pageNodes    = moduleConfig.GetNodes("PAGE");

                    // Which we can now parse for page definitions.
                    for (int i = 0; i < pageNodes.Length; i++)
                    {
                        // Mwahahaha.
                        try {
                            var newPage = new MonitorPage(i, pageNodes[i], this);
                            activePage = activePage ?? newPage;
                            if (newPage.isDefault)
                            {
                                activePage = newPage;
                            }
                            pages.Add(newPage);
                        } catch (ArgumentException e) {
                            JUtil.LogMessage(this, "Warning - {0}", e);
                        }
                    }

                    // Now that all pages are loaded, we can use the moment in the loop to suck in all the extra fonts.
                    foreach (string value in moduleConfig.GetValues("extraFont"))
                    {
                        fontTexture.Add(LoadFont(this, internalProp, value, true));
                    }

                    break;
                }
            }
            JUtil.LogMessage(this, "Done setting up pages, {0} pages ready.", pages.Count);

            // Load our state from storage...
            persistentVarName = "activePage" + internalProp.propID;
            persistence       = new PersistenceAccessor(part);
            int?activePageID = persistence.GetVar(persistentVarName);

            if (activePageID != null && activePageID.Value < pages.Count)
            {
                activePage = pages[activePageID.Value];
            }
            activePage.Active(true);

            // If we have global buttons, set them up.
            if (!string.IsNullOrEmpty(globalButtons))
            {
                string[] tokens = globalButtons.Split(',');
                for (int i = 0; i < tokens.Length; i++)
                {
                    string buttonName = tokens[i].Trim();
                    // Notice that holes in the global button list ARE legal.
                    if (!string.IsNullOrEmpty(buttonName))
                    {
                        SmarterButton.CreateButton(internalProp, buttonName, i, GlobalButtonClick, GlobalButtonRelease);
                    }
                }
            }

            audioOutput     = JUtil.SetupIVASound(internalProp, buttonClickSound, buttonClickVolume, false);
            startupComplete = true;
        }