private void ApplyChanges() { MASConfig.VerboseLogging = verboseLogging; MASConfig.LuaUpdatePriority = luaUpdatePriority; MASConfig.CameraTextureScale = cameraTextureScale; MASConfig.navigation.generalPropagation = generalPropagation; MASConfig.navigation.NDBPropagation = NDBPropagation; MASConfig.navigation.VORPropagation = VORPropagation; MASConfig.navigation.DMEPropagation = DMEPropagation; MASLoader.UpdateHorizonDistance(); }
/// <summary> /// Read our config settings, setting defaults where needed. /// </summary> /// <param name="node"></param> public override void OnLoad(ConfigNode node) { if (!node.TryGetValue("VerboseLogging", ref VerboseLogging)) { VerboseLogging = true; } if (!node.TryGetValue("HideGui", ref HideGui)) { HideGui = false; } if (!node.TryGetValue("ElectricCharge", ref ElectricCharge)) { ElectricCharge = "ElectricCharge"; } if (!node.TryGetValue("LuaUpdatePriority", ref LuaUpdatePriority)) { LuaUpdatePriority = 1; } if (!node.TryGetValue("CameraTextureScale", ref CameraTextureScale)) { CameraTextureScale = 0; } if (!node.TryGetValue("GeneralPropagation", ref navigation.generalPropagation)) { navigation.generalPropagation = 2.0f; } if (!node.TryGetValue("NDBPropagation", ref navigation.NDBPropagation)) { navigation.NDBPropagation = 1.0f; } if (!node.TryGetValue("VORPropagation", ref navigation.VORPropagation)) { navigation.VORPropagation = 1.2f; } if (!node.TryGetValue("DMEPropagation", ref navigation.DMEPropagation)) { navigation.DMEPropagation = 1.4f; } MASLoader.UpdateHorizonDistance(); }
internal MASComponentTextLabel(ConfigNode config, InternalProp prop, MASFlightComputer comp) : base(config, prop, comp) { string transform = string.Empty; if (!config.TryGetValue("transform", ref transform)) { throw new ArgumentException("Missing 'transform' in TEXT_LABEL " + name); } string fontName = string.Empty; if (!config.TryGetValue("font", ref fontName)) { throw new ArgumentException("Invalid or missing 'font' in TEXT_LABEL " + name); } string styleStr = string.Empty; FontStyle style = FontStyle.Normal; if (config.TryGetValue("style", ref styleStr)) { style = MdVTextMesh.FontStyle(styleStr); } string text = string.Empty; if (!config.TryGetValue("text", ref text)) { throw new ArgumentException("Invalid or missing 'text' in TEXT_LABEL " + name); } if (!config.TryGetValue("fontSize", ref fontSize)) { throw new ArgumentException("Invalid or missing 'fontSize' in TEXT_LABEL " + name); } Vector2 transformOffset = Vector2.zero; if (!config.TryGetValue("transformOffset", ref transformOffset)) { transformOffset = Vector2.zero; } Transform textObjTransform = prop.FindModelTransform(transform); Vector3 localScale = prop.transform.localScale; Transform offsetTransform = new GameObject().transform; offsetTransform.gameObject.name = Utility.ComposeObjectName(this.GetType().Name, name, prop.propID); offsetTransform.gameObject.layer = textObjTransform.gameObject.layer; offsetTransform.SetParent(textObjTransform, false); offsetTransform.Translate(transformOffset.x * localScale.x, transformOffset.y * localScale.y, 0.0f); textObj = offsetTransform.gameObject.AddComponent <MdVTextMesh>(); Font font = MASLoader.GetFont(fontName.Trim()); if (font == null) { throw new ArgumentNullException("Unable to load font " + fontName + " in TEXT_LABEL " + name); } float lineSpacing = 1.0f; if (!config.TryGetValue("lineSpacing", ref lineSpacing)) { lineSpacing = 1.0f; } float sizeScalar = 32.0f / (float)font.fontSize; textObj.SetFont(font, font.fontSize, fontSize * 0.00005f * sizeScalar); textObj.SetLineSpacing(lineSpacing); textObj.fontStyle = style; string passiveColorStr = string.Empty; if (!config.TryGetValue("passiveColor", ref passiveColorStr)) { throw new ArgumentException("Invalid or missing 'passiveColor' in TEXT_LABEL " + name); } else { Color32 namedColor; if (comp.TryGetNamedColor(passiveColorStr, out namedColor)) { passiveColor = namedColor; } else { string[] startColors = Utility.SplitVariableList(passiveColorStr); if (startColors.Length < 3 || startColors.Length > 4) { throw new ArgumentException("passiveColor does not contain 3 or 4 values in TEXT_LABEL " + name); } variableRegistrar.RegisterVariableChangeCallback(startColors[0], (double newValue) => { passiveColor.r = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); variableRegistrar.RegisterVariableChangeCallback(startColors[1], (double newValue) => { passiveColor.g = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); variableRegistrar.RegisterVariableChangeCallback(startColors[2], (double newValue) => { passiveColor.b = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); if (startColors.Length == 4) { variableRegistrar.RegisterVariableChangeCallback(startColors[3], (double newValue) => { passiveColor.a = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); } } } // Final validations bool usesMulticolor = false; string activeColorStr = string.Empty; if (config.TryGetValue("activeColor", ref activeColorStr)) { usesMulticolor = true; Color32 namedColor; if (comp.TryGetNamedColor(activeColorStr, out namedColor)) { activeColor = namedColor; } else { string[] startColors = Utility.SplitVariableList(activeColorStr); if (startColors.Length < 3 || startColors.Length > 4) { throw new ArgumentException("activeColor does not contain 3 or 4 values in TEXT_LABEL " + name); } variableRegistrar.RegisterVariableChangeCallback(startColors[0], (double newValue) => { activeColor.r = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); variableRegistrar.RegisterVariableChangeCallback(startColors[1], (double newValue) => { activeColor.g = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); variableRegistrar.RegisterVariableChangeCallback(startColors[2], (double newValue) => { activeColor.b = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); if (startColors.Length == 4) { variableRegistrar.RegisterVariableChangeCallback(startColors[3], (double newValue) => { activeColor.a = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); if (blend) { UpdateBlendColor(); } else { UpdateBooleanColor(); } }); } } } string anchor = string.Empty; if (!config.TryGetValue("anchor", ref anchor)) { anchor = string.Empty; } if (!string.IsNullOrEmpty(anchor)) { if (anchor == TextAnchor.LowerCenter.ToString()) { textObj.anchor = TextAnchor.LowerCenter; } else if (anchor == TextAnchor.LowerLeft.ToString()) { textObj.anchor = TextAnchor.LowerLeft; } else if (anchor == TextAnchor.LowerRight.ToString()) { textObj.anchor = TextAnchor.LowerRight; } else if (anchor == TextAnchor.MiddleCenter.ToString()) { textObj.anchor = TextAnchor.MiddleCenter; } else if (anchor == TextAnchor.MiddleLeft.ToString()) { textObj.anchor = TextAnchor.MiddleLeft; } else if (anchor == TextAnchor.MiddleRight.ToString()) { textObj.anchor = TextAnchor.MiddleRight; } else if (anchor == TextAnchor.UpperCenter.ToString()) { textObj.anchor = TextAnchor.UpperCenter; } else if (anchor == TextAnchor.UpperLeft.ToString()) { textObj.anchor = TextAnchor.UpperLeft; } else if (anchor == TextAnchor.UpperRight.ToString()) { textObj.anchor = TextAnchor.UpperRight; } else { Utility.LogError(this, "Unrecognized anchor '{0}' in config for {1} ({2})", anchor, prop.propID, prop.propName); } } string alignment = string.Empty; if (!config.TryGetValue("alignment", ref alignment)) { alignment = string.Empty; } if (!string.IsNullOrEmpty(alignment)) { if (alignment == TextAlignment.Center.ToString()) { textObj.alignment = TextAlignment.Center; } else if (alignment == TextAlignment.Left.ToString()) { textObj.alignment = TextAlignment.Left; } else if (alignment == TextAlignment.Right.ToString()) { textObj.alignment = TextAlignment.Right; } else { Utility.LogError(this, "Unrecognized alignment '{0}' in config for {1} ({2})", alignment, prop.propID, prop.propName); } } string emissive = string.Empty; config.TryGetValue("emissive", ref emissive); if (string.IsNullOrEmpty(emissive)) { if (usesMulticolor) { emissiveMode = EmissiveMode.active; } else { emissiveMode = EmissiveMode.always; } } else if (emissive.ToLower() == EmissiveMode.always.ToString()) { emissiveMode = EmissiveMode.always; } else if (emissive.ToLower() == EmissiveMode.never.ToString()) { emissiveMode = EmissiveMode.never; } else if (emissive.ToLower() == EmissiveMode.active.ToString()) { emissiveMode = EmissiveMode.active; } else if (emissive.ToLower() == EmissiveMode.passive.ToString()) { emissiveMode = EmissiveMode.passive; } else if (emissive.ToLower() == EmissiveMode.flash.ToString()) { emissiveMode = EmissiveMode.flash; } else { Utility.LogError(this, "Unrecognized emissive mode '{0}' in config for {1} ({2})", emissive, prop.propID, prop.propName); emissiveMode = EmissiveMode.always; } string variableName = string.Empty; if (!config.TryGetValue("variable", ref variableName) || string.IsNullOrEmpty(variableName)) { if (usesMulticolor) { throw new ArgumentException("Invalid or missing 'variable' in TEXT_LABEL " + name); } } else if (!usesMulticolor) { throw new ArgumentException("Invalid or missing 'activeColor' in TEXT_LABEL " + name); } config.TryGetValue("blend", ref blend); if (emissiveMode == EmissiveMode.flash) { if (blend == false && config.TryGetValue("flashRate", ref flashRate) && flashRate > 0.0f) { this.comp = comp; comp.RegisterFlashCallback(flashRate, FlashToggle); } else { emissiveMode = EmissiveMode.active; } } bool immutable = false; if (!config.TryGetValue("oneshot", ref immutable)) { immutable = false; } textObj.SetColor(passiveColor); textObj.SetText(text, immutable, false, comp, prop); UpdateShader(); if (!string.IsNullOrEmpty(variableName)) { variableRegistrar.RegisterVariableChangeCallback(variableName, VariableCallback); } }
/// <summary> /// Startup, initialize, configure, etc. /// </summary> public void Start() { if (HighLogic.LoadedSceneIsFlight) { try { MASFlightComputer comp = MASFlightComputer.Instance(internalProp.part); if (comp == null) { throw new ArgumentNullException("Failed to find MASFlightComputer initializing MASMonitor"); } variableRegistrar = new VariableRegistrar(comp, internalProp); if (string.IsNullOrEmpty(screenTransform)) { throw new ArgumentException("Missing 'transform' in MASMonitor"); } if (string.IsNullOrEmpty(layer)) { throw new ArgumentException("Missing 'layer' in MASMonitor"); } if (string.IsNullOrEmpty(font)) { throw new ArgumentException("Missing 'font' in MASMonitor"); } if (string.IsNullOrEmpty(textColor)) { throw new ArgumentException("Missing 'textColor' in MASMonitor"); } else { textColor_ = Utility.ParseColor32(textColor, comp); } if (string.IsNullOrEmpty(backgroundColor)) { throw new ArgumentException("Missing 'backgroundColor' in MASMonitor"); } else { backgroundColor_ = Utility.ParseColor32(backgroundColor, comp); } if (screenSize.x <= 0.0f || screenSize.y <= 0.0f) { throw new ArgumentException("Invalid 'screenSize' in MASMonitor"); } if (fontSize.x <= 0.0f || fontSize.y <= 0.0f) { throw new ArgumentException("Invalid 'fontSize' in MASMonitor"); } screenWidth = (int)screenSize.x; screenHeight = (int)screenSize.y; screenSpace = new GameObject(); screenSpace.name = Utility.ComposeObjectName(internalProp.propName, this.GetType().Name, screenSpace.GetInstanceID()); screenSpace.layer = drawingLayer; screenSpace.transform.position = Vector3.zero; screenSpace.SetActive(true); screen = new RenderTexture(screenWidth, screenHeight, 24, RenderTextureFormat.ARGB32); if (screen == null) { throw new ArgumentNullException("Failed to find create " + screenWidth + " x " + screenHeight + " render texture initializing MASMonitor"); } if (!screen.IsCreated()) { screen.Create(); screen.DiscardContents(); } Camera.onPreCull += EnablePage; Camera.onPostRender += DisablePage; screenCamera = screenSpace.AddComponent <Camera>(); screenCamera.enabled = true; // Enable = "auto-draw" screenCamera.orthographic = true; screenCamera.aspect = screenSize.x / screenSize.y; screenCamera.eventMask = 0; screenCamera.farClipPlane = 1.0f + depthDelta; screenCamera.nearClipPlane = depthDelta; screenCamera.orthographicSize = screenSize.y * 0.5f; screenCamera.cullingMask = 1 << drawingLayer; screenCamera.transparencySortMode = TransparencySortMode.Orthographic; screenCamera.transform.position = Vector3.zero; screenCamera.transform.LookAt(new Vector3(0.0f, 0.0f, maxDepth), Vector3.up); screenCamera.backgroundColor = backgroundColor_; screenCamera.clearFlags = CameraClearFlags.SolidColor; screenCamera.targetTexture = screen; Transform screenTransformLoc = internalProp.FindModelTransform(screenTransform); if (screenTransformLoc == null) { throw new ArgumentNullException("Failed to find screenTransform \"" + screenTransform + "\" initializing MASMonitor"); } Material screenMat = screenTransformLoc.GetComponent <Renderer>().material; string[] layers = layer.Split(); for (int i = layers.Length - 1; i >= 0; --i) { screenMat.SetTexture(layers[i].Trim(), screen); } defaultFont = MASLoader.GetFont(font.Trim()); if (!string.IsNullOrEmpty(style)) { defaultStyle = MdVTextMesh.FontStyle(style.Trim()); } ConfigNode moduleConfig = Utility.GetPropModuleConfigNode(internalProp.propName, ClassName); if (moduleConfig == null) { throw new ArgumentNullException("No ConfigNode found for MASMonitor in " + internalProp.propName + "!"); } // If an initialization script was supplied, call it. if (!string.IsNullOrEmpty(startupScript)) { Action startup = comp.GetAction(startupScript, internalProp); startup(); } string[] pages = moduleConfig.GetValues("page"); int numPages = pages.Length; for (int i = 0; i < numPages; ++i) { pages[i] = pages[i].Trim(); ConfigNode pageConfig = Utility.GetPageConfigNode(pages[i]); if (pageConfig == null) { throw new ArgumentException("No ConfigNode found for page " + pages[i] + " in MASMonitor in " + internalProp.propName + "!"); } // Parse the page node MASPage newPage = new MASPage(pageConfig, internalProp, comp, this, screenSpace.transform); if (i == 0) { // Select the default page as the current page currentPage = newPage; } newPage.SetPageActive(false); page.Add(pages[i], newPage); //Utility.LogMessage(this, "Page = {0}", pages[i]); } //HackWalkTransforms(screenSpace.transform, 0); if (!string.IsNullOrEmpty(monitorID)) { string variableName = "fc.GetPersistent(\"" + monitorID.Trim() + "\")"; pageSelector = variableRegistrar.RegisterVariableChangeCallback(variableName, PageChanged, false); // See if we have a saved page to restore. if (!string.IsNullOrEmpty(pageSelector.AsString()) && page.ContainsKey(pageSelector.AsString())) { currentPage = page[pageSelector.AsString()]; } comp.RegisterMonitor(monitorID, internalProp, this); } currentPage.SetPageActive(true); initialized = true; Utility.LogMessage(this, "Configuration complete in prop #{0} ({1}) with {2} pages", internalProp.propID, internalProp.propName, numPages); } catch (Exception e) { Utility.ComplainLoudly("MASMonitor configuration failed."); Utility.LogError(this, "Failed to configure prop #{0} ({1})", internalProp.propID, internalProp.propName); Utility.LogError(this, e.ToString()); } } }
internal MASPageText(ConfigNode config, InternalProp prop, MASFlightComputer comp, MASMonitor monitor, Transform pageRoot, float depth) : base(config, prop, comp) { if (!config.TryGetValue("text", ref text)) { string textfile = string.Empty; if (!config.TryGetValue("textfile", ref textfile)) { string rpmModText = string.Empty; if (!config.TryGetValue("textmethod", ref rpmModText)) { throw new ArgumentException("Unable to find 'text', 'textfile', or 'textmethod' in TEXT " + name); } string[] rpmMod = rpmModText.Split(':'); if (rpmMod.Length != 2) { throw new ArgumentException("Invalid 'textmethod' in TEXT " + name); } bool moduleFound = false; int numModules = prop.internalModules.Count; int moduleIndex; for (moduleIndex = 0; moduleIndex < numModules; ++moduleIndex) { if (prop.internalModules[moduleIndex].ClassName == rpmMod[0]) { moduleFound = true; break; } } if (moduleFound) { rpmModule = prop.internalModules[moduleIndex]; Type moduleType = prop.internalModules[moduleIndex].GetType(); MethodInfo method = moduleType.GetMethod(rpmMod[1]); if (method != null && method.GetParameters().Length == 2 && method.GetParameters()[0].ParameterType == typeof(int) && method.GetParameters()[1].ParameterType == typeof(int)) { rpmModuleTextMethod = DynamicMethodFactory.CreateFunc <object, int, int, string>(method); } } if (rpmModuleTextMethod != null) { this.comp = comp; this.prop = prop; } text = " "; } else { // Load text text = string.Join(Environment.NewLine, File.ReadAllLines(KSPUtil.ApplicationRootPath + "GameData/" + textfile.Trim(), Encoding.UTF8)); } } string localFonts = string.Empty; if (!config.TryGetValue("font", ref localFonts)) { localFonts = string.Empty; } string styleStr = string.Empty; FontStyle style = FontStyle.Normal; if (config.TryGetValue("style", ref styleStr)) { style = MdVTextMesh.FontStyle(styleStr); } else { style = monitor.defaultStyle; } Vector2 fontSize = Vector2.zero; if (!config.TryGetValue("fontSize", ref fontSize) || fontSize.x < 0.0f || fontSize.y < 0.0f) { fontSize = monitor.fontSize; } Color32 textColor; string textColorStr = string.Empty; if (!config.TryGetValue("textColor", ref textColorStr) || string.IsNullOrEmpty(textColorStr)) { textColor = monitor.textColor_; } else { textColor = Utility.ParseColor32(textColorStr, comp); } // Position is based on default font size fontScale = monitor.fontSize; // Position is based on local font size. //fontScale = fontSize; string variableName = string.Empty; if (config.TryGetValue("variable", ref variableName)) { variableName = variableName.Trim(); } // Set up our text. imageOrigin = pageRoot.position + new Vector3(monitor.screenSize.x * -0.5f, monitor.screenSize.y * 0.5f, depth); meshObject = new GameObject(); meshObject.name = Utility.ComposeObjectName(pageRoot.gameObject.name, this.GetType().Name, name, (int)(-depth / MASMonitor.depthDelta)); meshObject.layer = pageRoot.gameObject.layer; meshObject.transform.parent = pageRoot; meshObject.transform.position = imageOrigin; string positionString = string.Empty; if (config.TryGetValue("position", ref positionString)) { string[] positions = Utility.SplitVariableList(positionString); if (positions.Length != 2) { throw new ArgumentException("position does not contain 2 values in TEXT " + name); } variableRegistrar.RegisterVariableChangeCallback(positions[0], (double newValue) => { position.x = (float)newValue * fontScale.x; meshObject.transform.position = imageOrigin + new Vector3(position.x, -position.y, 0.0f); }); variableRegistrar.RegisterVariableChangeCallback(positions[1], (double newValue) => { position.y = (float)newValue * fontScale.y; meshObject.transform.position = imageOrigin + new Vector3(position.x, -position.y, 0.0f); }); } textObj = meshObject.gameObject.AddComponent <MdVTextMesh>(); Font font; if (string.IsNullOrEmpty(localFonts)) { font = monitor.defaultFont; } else { font = MASLoader.GetFont(localFonts.Trim()); } // We want to use a different shader for monitor displays. textObj.material = new Material(MASLoader.shaders["MOARdV/TextMonitor"]); textObj.SetFont(font, fontSize); textObj.SetColor(textColor); textObj.material.SetFloat(Shader.PropertyToID("_EmissiveFactor"), 1.0f); textObj.fontStyle = style; // text, immutable, preserveWhitespace, comp, prop textObj.SetText(text, false, true, comp, prop); RenderPage(false); if (!string.IsNullOrEmpty(variableName)) { // Disable the mesh if we're in variable mode meshObject.SetActive(false); variableRegistrar.RegisterVariableChangeCallback(variableName, VariableCallback); } else { currentState = true; } if (rpmModuleTextMethod != null) { comp.StartCoroutine(TextMethodUpdate()); } }
internal MASPageCompoundText(ConfigNode config, InternalProp prop, MASFlightComputer comp, MASMonitor monitor, Transform pageRoot, float depth) : base(config, prop, comp) { this.comp = comp; if (!config.TryGetValue("maxLines", ref maxLines)) { throw new ArgumentException("Missing 'maxLines' in COMPOUND_TEXT " + name); } if (maxLines < 1) { throw new ArgumentException("'maxLines' must be greater than zero in COMPOUND_TEXT " + name); } string localFonts = string.Empty; if (!config.TryGetValue("font", ref localFonts)) { localFonts = string.Empty; } string styleStr = string.Empty; FontStyle style = FontStyle.Normal; if (config.TryGetValue("style", ref styleStr)) { style = MdVTextMesh.FontStyle(styleStr); } else { style = monitor.defaultStyle; } Vector2 fontSize = Vector2.zero; if (!config.TryGetValue("fontSize", ref fontSize) || fontSize.x < 0.0f || fontSize.y < 0.0f) { fontSize = monitor.fontSize; } lineAdvance = fontSize.y; Color32 textColor; string textColorStr = string.Empty; if (!config.TryGetValue("textColor", ref textColorStr) || string.IsNullOrEmpty(textColorStr)) { textColor = monitor.textColor_; } else { textColor = Utility.ParseColor32(textColorStr, comp); } // Set up our text. textOrigin = pageRoot.position + new Vector3(monitor.screenSize.x * -0.5f, monitor.screenSize.y * 0.5f, depth); rootObject = new GameObject(); rootObject.name = Utility.ComposeObjectName(pageRoot.gameObject.name, this.GetType().Name, name, (int)(-depth / MASMonitor.depthDelta)); rootObject.layer = pageRoot.gameObject.layer; rootObject.transform.parent = pageRoot; rootObject.transform.position = textOrigin; string positionString = string.Empty; if (config.TryGetValue("position", ref positionString)) { string[] positions = Utility.SplitVariableList(positionString); if (positions.Length != 2) { throw new ArgumentException("position does not contain 2 values in COMPOUND_TEXT " + name); } variableRegistrar.RegisterVariableChangeCallback(positions[0], (double newValue) => { position.x = (float)newValue * monitor.fontSize.x; rootObject.transform.position = textOrigin + new Vector3(position.x, -position.y, 0.0f); }); variableRegistrar.RegisterVariableChangeCallback(positions[1], (double newValue) => { position.y = (float)newValue * monitor.fontSize.y; rootObject.transform.position = textOrigin + new Vector3(position.x, -position.y, 0.0f); }); } Font font; if (string.IsNullOrEmpty(localFonts)) { font = monitor.defaultFont; } else { font = MASLoader.GetFont(localFonts.Trim()); } List <CompoundPageText> textNodes = new List <CompoundPageText>(); ConfigNode[] textConfigNodes = config.GetNodes("TEXT"); foreach (ConfigNode textNode in textConfigNodes) { CompoundPageText cpt = new CompoundPageText(); if (!textNode.TryGetValue("name", ref cpt.name)) { cpt.name = "anonymous"; } string variableName = string.Empty; if (!textNode.TryGetValue("variable", ref variableName)) { variableName.Trim(); } string text = string.Empty; if (textNode.TryGetValue("text", ref text)) { cpt.textObject = new GameObject(); cpt.textObject.name = Utility.ComposeObjectName(pageRoot.gameObject.name, this.GetType().Name + textNodes.Count, cpt.name, (int)(-depth / MASMonitor.depthDelta)); cpt.textObject.layer = rootObject.layer; cpt.textObject.transform.parent = rootObject.transform; cpt.textObject.transform.position = rootObject.transform.position; cpt.textMesh = cpt.textObject.AddComponent <MdVTextMesh>(); cpt.textMesh.material = new Material(MASLoader.shaders["MOARdV/TextMonitor"]); cpt.textMesh.SetFont(font, fontSize); cpt.textMesh.SetColor(textColor); cpt.textMesh.material.SetFloat(Shader.PropertyToID("_EmissiveFactor"), 1.0f); cpt.textMesh.fontStyle = style; // text, immutable, preserveWhitespace, comp, prop cpt.textMesh.SetText(text, false, true, comp, prop); } // Process callbacks if (!string.IsNullOrEmpty(variableName)) { if (cpt.textObject != null) { cpt.textObject.SetActive(false); } variableRegistrar.RegisterVariableChangeCallback(variableName, (double newValue) => VariableCallback(newValue, cpt)); } else { cpt.currentState = true; if (!coroutineActive) { coroutineActive = true; comp.StartCoroutine(TextMethodUpdate()); } } textNodes.Add(cpt); } textElements = textNodes.ToArray(); string masterVariableName = string.Empty; if (config.TryGetValue("variable", ref masterVariableName)) { rootObject.SetActive(false); variableRegistrar.RegisterVariableChangeCallback(masterVariableName, VariableCallback); } else { rootObject.SetActive(true); } RenderPage(false); }
internal MASPageImage(ConfigNode config, InternalProp prop, MASFlightComputer comp, MASMonitor monitor, Transform pageRoot, float depth) : base(config, prop, comp) { string textureName = string.Empty; if (!config.TryGetValue("texture", ref textureName)) { throw new ArgumentException("Unable to find 'texture' in IMAGE " + name); } Texture2D mainTexture = null; if (textureName == "%MAP_ICON%") { mainTexture = MASLoader.OrbitIconsAtlas(); } else if (textureName == "%NAVBALL_ICON%") { mainTexture = GameDatabase.Instance.GetTexture("Squad/Props/IVANavBall/ManeuverNode_vectors", false); } else { if (textureName == "%FLAG%") { textureName = prop.part.flagURL; } mainTexture = GameDatabase.Instance.GetTexture(textureName, false); } if (mainTexture == null) { throw new ArgumentException("Unable to find 'texture' " + textureName + " for IMAGE " + name); } bool wrap = true; if (config.TryGetValue("wrap", ref wrap)) { mainTexture.wrapMode = (wrap) ? TextureWrapMode.Repeat : TextureWrapMode.Clamp; } string variableName = string.Empty; if (config.TryGetValue("variable", ref variableName)) { variableName = variableName.Trim(); } string rotationVariableName = string.Empty; if (config.TryGetValue("rotation", ref rotationVariableName)) { config.TryGetValue("rotationOffset", ref rotationOffset); } // Need Mesh for UpdateVertices. mesh = new Mesh(); string sizeString = string.Empty; if (!config.TryGetValue("size", ref sizeString)) { size = new Vector2(mainTexture.width, mainTexture.height); UpdateVertices(); } else { string[] sizes = Utility.SplitVariableList(sizeString); if (sizes.Length != 2) { throw new ArgumentException("Invalid number of values for 'size' in IMAGE " + name); } variableRegistrar.RegisterVariableChangeCallback(sizes[0], (double newValue) => { size.x = (float)newValue; UpdateVertices(); }); variableRegistrar.RegisterVariableChangeCallback(sizes[1], (double newValue) => { size.y = (float)newValue; UpdateVertices(); }); } imageOrigin = pageRoot.position + new Vector3(monitor.screenSize.x * -0.5f, monitor.screenSize.y * 0.5f, depth); // Need imageObject for UpdatePosition. imageObject = new GameObject(); imageObject.transform.parent = pageRoot; string positionString = string.Empty; if (!config.TryGetValue("position", ref positionString)) { position = Vector2.zero; imageObject.transform.position = imageOrigin + new Vector3(position.x + rotationOffset.x + size.x * 0.5f, -(position.y + rotationOffset.y + size.y * 0.5f), 0.0f); } else { string[] pos = Utility.SplitVariableList(positionString); if (pos.Length != 2) { throw new ArgumentException("Invalid number of values for 'position' in IMAGE " + name); } variableRegistrar.RegisterVariableChangeCallback(pos[0], (double newValue) => { position.x = (float)newValue; imageObject.transform.position = imageOrigin + new Vector3(position.x + rotationOffset.x + size.x * 0.5f, -(position.y + rotationOffset.y + size.y * 0.5f), 0.0f); }); variableRegistrar.RegisterVariableChangeCallback(pos[1], (double newValue) => { position.y = (float)newValue; imageObject.transform.position = imageOrigin + new Vector3(position.x + rotationOffset.x + size.x * 0.5f, -(position.y + rotationOffset.y + size.y * 0.5f), 0.0f); }); } // Set up our surface. imageObject.name = Utility.ComposeObjectName(pageRoot.gameObject.name, this.GetType().Name, name, (int)(-depth / MASMonitor.depthDelta)); imageObject.layer = pageRoot.gameObject.layer; // add renderer stuff MeshFilter meshFilter = imageObject.AddComponent <MeshFilter>(); meshRenderer = imageObject.AddComponent <MeshRenderer>(); mesh.uv = new[] { new Vector2(0.0f, 1.0f), Vector2.one, Vector2.zero, new Vector2(1.0f, 0.0f), }; mesh.triangles = new[] { 0, 1, 2, 1, 3, 2 }; mesh.RecalculateBounds(); mesh.UploadMeshData(false); meshFilter.mesh = mesh; imageMaterial = new Material(MASLoader.shaders["MOARdV/Monitor"]); imageMaterial.mainTexture = mainTexture; meshRenderer.material = imageMaterial; RenderPage(false); currentBlend = 0.0f; passiveColor = Color.white; activeColor = Color.white; string passiveColorName = string.Empty; if (config.TryGetValue("passiveColor", ref passiveColorName)) { Color32 color32; if (comp.TryGetNamedColor(passiveColorName, out color32)) { passiveColor = color32; } else { string[] startColors = Utility.SplitVariableList(passiveColorName); if (startColors.Length < 3 || startColors.Length > 4) { throw new ArgumentException("'passiveColor' does not contain 3 or 4 values in IMAGE " + name); } variableRegistrar.RegisterVariableChangeCallback(startColors[0], (double newValue) => { passiveColor.r = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); variableRegistrar.RegisterVariableChangeCallback(startColors[1], (double newValue) => { passiveColor.g = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); variableRegistrar.RegisterVariableChangeCallback(startColors[2], (double newValue) => { passiveColor.b = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); if (startColors.Length == 4) { variableRegistrar.RegisterVariableChangeCallback(startColors[3], (double newValue) => { passiveColor.a = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); } } } string colorVariableName = string.Empty; if (config.TryGetValue("colorVariable", ref colorVariableName)) { if (string.IsNullOrEmpty(passiveColorName)) { throw new ArgumentException("'colorVariable' found, but no 'passiveColor' in IMAGE " + name); } string activeColorName = string.Empty; if (!config.TryGetValue("activeColor", ref activeColorName)) { throw new ArgumentException("'colorVariable' found, but no 'activeColor' in IMAGE " + name); } string colorRangeString = string.Empty; if (config.TryGetValue("colorRange", ref colorRangeString)) { string[] colorRanges = Utility.SplitVariableList(colorRangeString); if (colorRanges.Length != 2) { throw new ArgumentException("Expected 2 values for 'colorRange' in IMAGE " + name); } variableRegistrar.RegisterVariableChangeCallback(colorRanges[0], (double newValue) => colorRange1 = (float)newValue); variableRegistrar.RegisterVariableChangeCallback(colorRanges[1], (double newValue) => colorRange2 = (float)newValue); bool colorBlend = false; if (config.TryGetValue("colorBlend", ref colorBlend) && colorBlend == true) { variableRegistrar.RegisterVariableChangeCallback(colorVariableName, (double newValue) => { float newBlend = Mathf.InverseLerp(colorRange1, colorRange2, (float)newValue); if (!Mathf.Approximately(newBlend, currentBlend)) { currentBlend = newBlend; UpdateColor(); } }); } else { variableRegistrar.RegisterVariableChangeCallback(colorVariableName, (double newValue) => { float newBlend = (newValue.Between(colorRange1, colorRange2)) ? 1.0f : 0.0f; if (newBlend != currentBlend) { currentBlend = newBlend; UpdateColor(); } }); } } else { variableRegistrar.RegisterVariableChangeCallback(colorVariableName, (double newValue) => { float newBlend = (newValue > 0.0) ? 1.0f : 0.0f; if (newBlend != currentBlend) { currentBlend = newBlend; UpdateColor(); } }); } Color32 color32; if (comp.TryGetNamedColor(activeColorName, out color32)) { activeColor = color32; } else { string[] activeColors = Utility.SplitVariableList(activeColorName); if (activeColors.Length < 3 || activeColors.Length > 4) { throw new ArgumentException("'activeColor' does not contain 3 or 4 values in IMAGE " + name); } variableRegistrar.RegisterVariableChangeCallback(activeColors[0], (double newValue) => { activeColor.r = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); variableRegistrar.RegisterVariableChangeCallback(activeColors[1], (double newValue) => { activeColor.g = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); variableRegistrar.RegisterVariableChangeCallback(activeColors[2], (double newValue) => { activeColor.b = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); if (activeColors.Length == 4) { variableRegistrar.RegisterVariableChangeCallback(activeColors[3], (double newValue) => { activeColor.a = Mathf.Clamp01((float)newValue * (1.0f / 255.0f)); UpdateColor(); }); } } } // In case fixed colors are being used. UpdateColor(); string uvTilingString = string.Empty; if (config.TryGetValue("tiling", ref uvTilingString)) { string[] uvTile = Utility.SplitVariableList(uvTilingString); if (uvTile.Length != 2) { throw new ArgumentException("'tiling' does not contain 2 values in IMAGE " + name); } variableRegistrar.RegisterVariableChangeCallback(uvTile[0], (double newValue) => { float rescale = Mathf.Max((float)newValue, 0.0f); if (!Mathf.Approximately(rescale, uvScale.x)) { uvScale.x = rescale; imageMaterial.SetTextureScale("_MainTex", uvScale); } }); variableRegistrar.RegisterVariableChangeCallback(uvTile[1], (double newValue) => { float rescale = Mathf.Max((float)newValue, 0.0f); if (!Mathf.Approximately(rescale, uvScale.y)) { uvScale.y = rescale; imageMaterial.SetTextureScale("_MainTex", uvScale); } }); } string uvShiftString = string.Empty; if (config.TryGetValue("uvShift", ref uvShiftString)) { string[] uvShifting = Utility.SplitVariableList(uvShiftString); if (uvShifting.Length != 2) { throw new ArgumentException("'uvShift' does not contain 2 values in IMAGE " + name); } variableRegistrar.RegisterVariableChangeCallback(uvShifting[0], (double newValue) => { float reshift = (float)newValue; uvShift.x = reshift; imageMaterial.SetTextureOffset("_MainTex", uvShift); }); variableRegistrar.RegisterVariableChangeCallback(uvShifting[1], (double newValue) => { float reshift = (float)newValue; uvShift.y = reshift; imageMaterial.SetTextureOffset("_MainTex", uvShift); }); } if (!string.IsNullOrEmpty(variableName)) { // Disable the mesh if we're in variable mode imageObject.SetActive(false); variableRegistrar.RegisterVariableChangeCallback(variableName, VariableCallback); } else { imageObject.SetActive(true); } if (!string.IsNullOrEmpty(rotationVariableName)) { variableRegistrar.RegisterVariableChangeCallback(rotationVariableName, RotationCallback); } }
internal MASPageRollingDigit(ConfigNode config, InternalProp prop, MASFlightComputer comp, MASMonitor monitor, Transform pageRoot, float depth) : base(config, prop, comp) { string localFonts = string.Empty; if (!config.TryGetValue("font", ref localFonts)) { localFonts = string.Empty; } string styleStr = string.Empty; style = FontStyle.Normal; if (config.TryGetValue("style", ref styleStr)) { style = MdVTextMesh.FontStyle(styleStr); } else { style = monitor.defaultStyle; } Vector2 fontDimensions = Vector2.zero; if (!config.TryGetValue("fontSize", ref fontDimensions) || fontDimensions.x < 0.0f || fontDimensions.y < 0.0f) { fontDimensions = monitor.fontSize; } Color32 textColor; string textColorStr = string.Empty; if (!config.TryGetValue("textColor", ref textColorStr) || string.IsNullOrEmpty(textColorStr)) { textColor = monitor.textColor_; } else { textColor = Utility.ParseColor32(textColorStr, comp); } // Position is based on default font size Vector2 fontScale = monitor.fontSize; string variableName = string.Empty; if (config.TryGetValue("variable", ref variableName)) { variableName = variableName.Trim(); } if (string.IsNullOrEmpty(localFonts)) { font = monitor.defaultFont; } else { font = MASLoader.GetFont(localFonts.Trim()); } // Set up our text. imageOrigin = pageRoot.position + new Vector3(monitor.screenSize.x * -0.5f, monitor.screenSize.y * 0.5f, depth); meshObject = new GameObject(); meshObject.name = Utility.ComposeObjectName(pageRoot.gameObject.name, this.GetType().Name, name, (int)(-depth / MASMonitor.depthDelta)); meshObject.layer = pageRoot.gameObject.layer; meshObject.transform.parent = pageRoot; meshObject.transform.position = imageOrigin; meshFilter = meshObject.AddComponent <MeshFilter>(); meshRenderer = meshObject.AddComponent <MeshRenderer>(); meshRenderer.material = new Material(MASLoader.shaders["MOARdV/TextMonitor"]); meshRenderer.material.mainTexture = font.material.mainTexture; string positionString = string.Empty; if (config.TryGetValue("position", ref positionString)) { string[] positions = Utility.SplitVariableList(positionString); if (positions.Length != 2) { throw new ArgumentException("position does not contain 2 values in ROLLING_DIGIT " + name); } variableRegistrar.RegisterVariableChangeCallback(positions[0], (double newValue) => { position.x = (float)newValue * fontScale.x; meshObject.transform.position = imageOrigin + new Vector3(position.x, -position.y, 0.0f); }); variableRegistrar.RegisterVariableChangeCallback(positions[1], (double newValue) => { position.y = (float)newValue * fontScale.y; meshObject.transform.position = imageOrigin + new Vector3(position.x, -position.y, 0.0f); }); } int maxDigits = 0; if (!config.TryGetValue("maxDigits", ref maxDigits)) { throw new ArgumentException("'maxDigits' missing in ROLLING_DIGIT " + name); } if (!config.TryGetValue("numRolling", ref numRolling)) { throw new ArgumentException("'numRolling' missing in ROLLING_DIGIT " + name); } if (numRolling < 1) { throw new ArgumentException("numRolling must be greater than zero in ROLLING_DIGIT " + name); } valueScalar = Mathf.Pow(10.0f, numRolling); numDigits = maxDigits - numRolling; if (numDigits < 0) { throw new ArgumentException("'numRolling' must be less than 'maxDigits' in ROLLING_DIGIT " + name); } scrollingVerticesOffset = 4 * numDigits; upperLimit = Mathf.Pow(10.0f, maxDigits) - 1.0f; lowerLimit = -Mathf.Pow(10.0f, maxDigits - 1) + 1.0f; bool padZero = false; if (!config.TryGetValue("padZero", ref padZero)) { padZero = false; } int numVertices = 4 * numDigits + 12 * numRolling; int numIndices = 6 * numDigits + 18 * numRolling; vertices = new Vector3[numVertices]; colors32 = new Color32[numVertices]; tangents = new Vector4[numVertices]; uv = new Vector2[numVertices]; triangles = new int[numIndices]; // These values are invariant: Vector4 tangent = new Vector4(1.0f, 0.0f, 0.0f, 1.0f); for (int i = 0; i < numVertices; ++i) { colors32[i] = textColor; tangents[i] = tangent; } for (int i = 0; i < numIndices / 6; ++i) { triangles[i * 6 + 0] = i * 4 + 0; triangles[i * 6 + 1] = i * 4 + 3; triangles[i * 6 + 2] = i * 4 + 2; triangles[i * 6 + 3] = i * 4 + 0; triangles[i * 6 + 4] = i * 4 + 1; triangles[i * 6 + 5] = i * 4 + 3; } if (numDigits > 0) { StringBuilder sb = StringBuilderCache.Acquire(); sb.AppendFormat("{{0,{0}:0", numDigits); if (padZero) { sb.Append('0', numDigits - 1); } sb.Append("}"); digitsFormat = sb.ToStringAndRelease(); } string valueString = string.Empty; if (!config.TryGetValue("value", ref valueString)) { throw new ArgumentException("'value' missing in ROLLING_DIGIT " + name); } variableRegistrar.RegisterVariableChangeCallback(valueString, ValueCallback); RenderPage(false); if (!string.IsNullOrEmpty(variableName)) { // Disable the mesh if we're in variable mode meshObject.SetActive(false); variableRegistrar.RegisterVariableChangeCallback(variableName, VariableCallback); } else { currentState = true; } dynamic = font.dynamic; float characterScalar; if (dynamic) { characterScalar = fontDimensions.y / (float)font.lineHeight; this.fontSize = font.fontSize; } else { // Unfortunately, there doesn't seem to be a way to set the font metrics when // creating a bitmap font, so I have to play games here by fetching the values // I stored in the character info. CharacterInfo ci = font.characterInfo[0]; characterScalar = fontDimensions.y / (float)ci.glyphHeight; this.fontSize = ci.glyphHeight; } this.fixedAdvance = (int)fontDimensions.x; this.fixedLineSpacing = Mathf.Floor(fontDimensions.y); this.characterScalar = characterScalar; ascent = (dynamic) ? font.ascent : (int)(0.8125f * fixedLineSpacing); yMaxLimit = 0.5f * fixedLineSpacing; yMinLimit = -1.5f * fixedLineSpacing; Font.textureRebuilt += FontRebuiltCallback; font.RequestCharactersInTexture(charactersUsed, fontSize, style); digitsChanged = true; fractionChanged = true; }
internal MASPageMenu(ConfigNode config, InternalProp prop, MASFlightComputer comp, MASMonitor monitor, Transform pageRoot, float depth) : base(config, prop, comp) { this.prop = prop; this.comp = comp; if (!config.TryGetValue("maxLines", ref maxLines)) { maxLines = int.MaxValue; } if (maxLines < 1) { throw new ArgumentException("'maxLines' must be greater than zero in MENU " + name); } int itemPositionShift = 0; if (!config.TryGetValue("itemPositionShift", ref itemPositionShift)) { itemPositionShift = 0; } if (!config.TryGetValue("cursorPersistentName", ref cursorPersistentName)) { throw new ArgumentException("Missing 'cursorPersistentName' in MENU " + name); } config.TryGetValue("upSoftkey", ref upSoftkey); config.TryGetValue("downSoftkey", ref downSoftkey); config.TryGetValue("enterSoftkey", ref enterSoftkey); config.TryGetValue("homeSoftkey", ref homeSoftkey); config.TryGetValue("endSoftkey", ref endSoftkey); string localFonts = string.Empty; if (!config.TryGetValue("font", ref localFonts)) { localFonts = string.Empty; } string styleStr = string.Empty; style = FontStyle.Normal; if (config.TryGetValue("style", ref styleStr)) { style = MdVTextMesh.FontStyle(styleStr); } else { style = monitor.defaultStyle; } fontSize = Vector2.zero; if (!config.TryGetValue("fontSize", ref fontSize) || fontSize.x < 0.0f || fontSize.y < 0.0f) { fontSize = monitor.fontSize; } charAdvance = fontSize.x * itemPositionShift; lineAdvance = fontSize.y; defaultColor = monitor.textColor_; Color32 cursorColor; string cursorColorStr = string.Empty; if (!config.TryGetValue("cursorColor", ref cursorColorStr) || string.IsNullOrEmpty(cursorColorStr)) { cursorColor = monitor.textColor_; } else { cursorColor = Utility.ParseColor32(cursorColorStr, comp); } // Set up our text. textOrigin = pageRoot.position + new Vector3(monitor.screenSize.x * -0.5f, monitor.screenSize.y * 0.5f, depth); rootObject = new GameObject(); rootObject.name = Utility.ComposeObjectName(pageRoot.gameObject.name, this.GetType().Name, name, (int)(-depth / MASMonitor.depthDelta)); rootObject.layer = pageRoot.gameObject.layer; rootObject.transform.parent = pageRoot; rootObject.transform.position = textOrigin; string positionString = string.Empty; if (config.TryGetValue("position", ref positionString)) { string[] positions = Utility.SplitVariableList(positionString); if (positions.Length != 2) { throw new ArgumentException("position does not contain 2 values in MENU " + name); } variableRegistrar.RegisterVariableChangeCallback(positions[0], (double newValue) => { position.x = (float)newValue * monitor.fontSize.x; rootObject.transform.position = textOrigin + new Vector3(position.x, -position.y, 0.0f); updateMenu = true; }); variableRegistrar.RegisterVariableChangeCallback(positions[1], (double newValue) => { position.y = (float)newValue * monitor.fontSize.y; rootObject.transform.position = textOrigin + new Vector3(position.x, -position.y, 0.0f); updateMenu = true; }); } if (string.IsNullOrEmpty(localFonts)) { font = monitor.defaultFont; } else { font = MASLoader.GetFont(localFonts.Trim()); } cursorObject = new GameObject(); cursorObject.name = rootObject.name + "_cursor"; cursorObject.layer = rootObject.layer; cursorObject.transform.parent = rootObject.transform; cursorObject.transform.position = textOrigin; cursorText = cursorObject.AddComponent <MdVTextMesh>(); cursorText.material = new Material(MASLoader.shaders["MOARdV/TextMonitor"]); cursorText.SetFont(font, fontSize); cursorText.SetColor(cursorColor); cursorText.material.SetFloat(Shader.PropertyToID("_EmissiveFactor"), 1.0f); cursorText.fontStyle = style; string cursorPrompt = string.Empty; config.TryGetValue("cursor", ref cursorPrompt); // text, immutable, preserveWhitespace, comp, prop cursorText.SetText(cursorPrompt, false, true, comp, prop); string itemCountStr = string.Empty; config.TryGetValue("itemCount", ref itemCountStr); List <MenuItem> itemNodes = new List <MenuItem>(); ConfigNode[] menuItemConfigNodes = config.GetNodes("ITEM"); foreach (ConfigNode itemNode in menuItemConfigNodes) { try { MenuItem cpt = new MenuItem(itemNode, rootObject, font, fontSize, style, defaultColor, comp, prop, variableRegistrar, itemNodes.Count); itemNodes.Add(cpt); } catch (Exception e) { Utility.LogError(this, "Exception creating ITEM in MENU " + name); Utility.LogError(this, e.ToString()); } } if (itemNodes.Count == 0) { throw new ArgumentException("No valid ITEM nodes in MENU " + name); } menuItems = itemNodes.ToArray(); if (string.IsNullOrEmpty(itemCountStr)) { numMenuItems = menuItems.Length; softkeyUpAction = comp.GetAction(string.Format("fc.AddPersistentWrapped(\"{0}\", -1, 0, {1})", cursorPersistentName, numMenuItems), prop); softkeyDownAction = comp.GetAction(string.Format("fc.AddPersistentWrapped(\"{0}\", 1, 0, {1})", cursorPersistentName, numMenuItems), prop); softkeyEndAction = comp.GetAction(string.Format("fc.SetPersistent(\"{0}\", {1})", cursorPersistentName, numMenuItems - 1), prop); } else if (itemNodes.Count > 1) { throw new ArgumentException("Only one valid ITEM node may be used in dynamic MENU " + name); } else { dynamicMenuTemplate = menuItemConfigNodes[0].CreateCopy(); } variableRegistrar.RegisterVariableChangeCallback(string.Format("fc.GetPersistentAsNumber(\"{0}\")", cursorPersistentName), CursorMovedCallback); softkeyHomeAction = comp.GetAction(string.Format("fc.SetPersistent(\"{0}\", 0)", cursorPersistentName), prop); if (!string.IsNullOrEmpty(itemCountStr)) { variableRegistrar.RegisterVariableChangeCallback(itemCountStr, MenuCountCallback); } string masterVariableName = string.Empty; if (config.TryGetValue("variable", ref masterVariableName)) { rootObject.SetActive(false); variableRegistrar.RegisterVariableChangeCallback(masterVariableName, VariableCallback); } else { currentState = true; rootObject.SetActive(true); } RenderPage(false); }