public AttachNodeBaseData(String nodeData) { String[] dataVals = nodeData.Split(new String[] { "," }, StringSplitOptions.None); position = new Vector3(ROLUtils.safeParseFloat(dataVals[0].Trim()), ROLUtils.safeParseFloat(dataVals[1].Trim()), ROLUtils.safeParseFloat(dataVals[2].Trim())); orientation = new Vector3(ROLUtils.safeParseFloat(dataVals[3].Trim()), ROLUtils.safeParseFloat(dataVals[4].Trim()), ROLUtils.safeParseFloat(dataVals[5].Trim())); size = dataVals.Length > 6 ? ROLUtils.safeParseInt(dataVals[6]) : 4; }
private void Initialize() { //MonoBehaviour.print("NodeFairingInit: "+fairingCreated+ " :: " +fairingForceDisabled+ " :: "+fairingJettisoned + " :: " +fairingEnabled); if (rendersToRemove != null && rendersToRemove.Length > 0) { ROLUtils.removeTransforms(part, ROLUtils.parseCSV(rendersToRemove)); } LoadFairingData(ROLConfigNodeUtils.parseConfigNode(configNodeData)); if (externalUpdateData != null) { UpdateFromExternalData(externalUpdateData); } if (fairingCreated || (fairingEnabled && !fairingJettisoned && !fairingForceDisabled && string.IsNullOrEmpty(nodeName)))//previously existed, recreate it, or should exist by default values in the config { BuildFairing(); UpdateFairingNodes(); if (!string.IsNullOrEmpty(nodeName)) { AttachNode n = part.FindAttachNode(nodeName); if (n != null && n.attachedPart != null) { prevAttachedPart = n.attachedPart; //MonoBehaviour.print("Setting initial attached part to: " + prevAttachedPart); } } } else if (!fairingJettisoned && !fairingForceDisabled && !string.IsNullOrEmpty(nodeName))//else could potentially be activated by a node...check for activation { needsStatusUpdate = true; } UpdateTextureSet(false); needsGuiUpdate = true; //MonoBehaviour.print("NodeFairingInit End: " + fairingCreated + " :: " + fairingForceDisabled + " :: " + fairingJettisoned + " :: " + fairingEnabled); }
public void SetOpacity(float value) { opacity = value; if (rootObject != null) { ROLUtils.setOpacityRecursive(rootObject.transform, value); } }
public void LoadPersistence(String data) { String[] csv = ROLUtils.parseCSV(data); topY = ROLUtils.safeParseFloat(csv[0]); bottomY = ROLUtils.safeParseFloat(csv[1]); topRadius = ROLUtils.safeParseFloat(csv[2]); bottomRadius = ROLUtils.safeParseFloat(csv[3]); noseNode = csv[4]; mountNode = csv[5]; }
public static bool[] ROLGetBoolValues(this ConfigNode node, String name) { String[] values = node.GetValues(name); int len = values.Length; bool[] vals = new bool[len]; for (int i = 0; i < len; i++) { vals[i] = ROLUtils.safeParseBool(values[i]); } return(vals); }
public static Vector3 ROLGetVector3(this ConfigNode node, String name, Vector3 defaultValue) { String value = node.GetValue(name); if (value == null) { return(defaultValue); } String[] vals = value.Split(','); if (vals.Length < 3) { ROLLog.error("ERROR parsing values for Vector3 from input: " + value + ". found less than 3 values, cannot create Vector3"); return(defaultValue); } return(new Vector3((float)ROLUtils.safeParseDouble(vals[0]), (float)ROLUtils.safeParseDouble(vals[1]), (float)ROLUtils.safeParseDouble(vals[2]))); }
public static float[] ROLGetFloatValues(this ConfigNode node, String name, float[] defaults) { String baseVal = node.ROLGetStringValue(name); if (!String.IsNullOrEmpty(baseVal)) { String[] split = baseVal.Split(new char[] { ',' }); float[] vals = new float[split.Length]; for (int i = 0; i < split.Length; i++) { vals[i] = ROLUtils.safeParseFloat(split[i]); } return(vals); } return(defaults); }
public static int[] ROLGetIntValues(this ConfigNode node, string name, int[] defaultValues = null) { int[] values = defaultValues; string[] stringValues = node.GetValues(name); if (stringValues == null || stringValues.Length == 0) { return(values); } int len = stringValues.Length; values = new int[len]; for (int i = 0; i < len; i++) { values[i] = ROLUtils.safeParseInt(stringValues[i]); } return(values); }
/// <summary> /// Initialization method. Sets up model modules, loads their configs from the input config node. Does all initial linking of part-modules.<para/> /// Does NOT set up their UI interaction -- that is all handled during OnStart() /// </summary> private void Initialize() { if (initialized) { return; } initialized = true; prevLength = panelLength; prevWidth = panelWidth; prevScale = panelScale; coreNodeNames = ROLUtils.parseCSV(coreManagedNodes); // Model-Module Setup / Initialization ConfigNode node = ROLConfigNodeUtils.parseConfigNode(configNodeData); // List of CORE model nodes from config // each one may contain multiple 'model=modelDefinitionName' entries // but must contain no more than a single 'variant' entry. // If no variant is specified, they are added to the 'Default' variant. ConfigNode[] coreDefNodes = node.GetNodes("CORE"); List <ModelDefinitionLayoutOptions> coreDefList = new List <ModelDefinitionLayoutOptions>(); foreach (ConfigNode cn in coreDefNodes) { string variantName = cn.ROLGetStringValue("variant", "Default"); coreDefs = ROLModelData.getModelDefinitionLayouts(cn.ROLGetStringValues("model")); coreDefList.AddUniqueRange(coreDefs); ModelDefinitionVariantSet mdvs = GetVariantSet(variantName); mdvs.AddModels(coreDefs); } coreDefs = coreDefList.ToArray(); coreModule = new ROLModelModule <ModuleROSolar>(part, this, ROLUtils.GetRootTransform(part, "ModuleROSolar-CORE"), ModelOrientation.CENTRAL, nameof(currentCore), null, null, null); coreModule.name = "ModuleROSolar-Core"; coreModule.getSymmetryModule = m => m.coreModule; coreModule.getValidOptions = () => GetVariantSet(currentVariant).definitions; coreModule.setupModelList(coreDefs); coreModule.setupModel(); UpdateModulePositions(); }
public GameObject[] generatePanels(Transform parent) { //each panelEdgeGroup should now contain the vertical edge loops for each vertical panel grouping //from here, need to generate meshes for panels depending upon splitPanels setting int len = panelGroups.Length; GameObject[] gos = new GameObject[panels]; for (int i = 0; i < len; i++) { gos[i] = parent.ROLFindOrCreate("FairingPanel-" + i).gameObject; ROLUtils.destroyChildren(gos[i].transform);//remove any existing colliders MeshFilter mf = gos[i].GetComponent <MeshFilter>(); if (mf == null) { mf = gos[i].AddComponent <MeshFilter>(); } mf.mesh = panelGroups[i].generatePanels(offset, outsideUV, insideUV, edgesUV); if (colliders) { GameObject[] cols = panelGroups[i].generateColliders(offset, facesPerCollider); for (int k = 0; k < cols.Length; k++) { cols[k].transform.NestToParent(gos[i].transform); } } MeshRenderer mr = gos[i].GetComponent <MeshRenderer>(); if (mr == null) { mr = gos[i].AddComponent <MeshRenderer>(); } gos[i].transform.parent = parent; gos[i].transform.localPosition = Vector3.zero; gos[i].transform.rotation = parent.rotation; } Transform[] trs; for (int i = len; i < 8; i++)//destroy extra unused panels { trs = parent.transform.ROLFindChildren("FairingPanel-" + i); for (int k = 0; k < trs.Length; k++) { GameObject.DestroyImmediate(trs[k].gameObject); } } return(gos); }
public static FloatCurve ROLGetFloatCurve(this ConfigNode node, String name, FloatCurve defaultValue = null) { FloatCurve curve = new FloatCurve(); if (node.HasNode(name)) { ConfigNode curveNode = node.GetNode(name); String[] values = curveNode.GetValues("key"); int len = values.Length; String[] splitValue; float a, b, c, d; for (int i = 0; i < len; i++) { splitValue = Regex.Replace(values[i], @"\s+", " ").Split(' '); if (splitValue.Length > 2) { a = ROLUtils.safeParseFloat(splitValue[0]); b = ROLUtils.safeParseFloat(splitValue[1]); c = ROLUtils.safeParseFloat(splitValue[2]); d = ROLUtils.safeParseFloat(splitValue[3]); curve.Add(a, b, c, d); } else { a = ROLUtils.safeParseFloat(splitValue[0]); b = ROLUtils.safeParseFloat(splitValue[1]); curve.Add(a, b); } } } else if (defaultValue != null) { foreach (Keyframe f in defaultValue.Curve.keys) { curve.Add(f.time, f.value, f.inTangent, f.outTangent); } } else { curve.Add(0, 0); curve.Add(1, 1); } return(curve); }
public static Material loadMaterial(String diffuse, String normal, String emissive, String shader) { Material material; Texture diffuseTexture = ROLUtils.findTexture(diffuse, false); Texture normalTexture = String.IsNullOrEmpty(normal) ? null : ROLUtils.findTexture(normal, true); Texture emissiveTexture = String.IsNullOrEmpty(emissive) ? null : ROLUtils.findTexture(emissive, false); material = new Material(Shader.Find(shader)); material.SetTexture("_MainTex", diffuseTexture); if (normalTexture != null) { material.SetTexture("_BumpMap", normalTexture); } if (emissiveTexture != null) { material.SetTexture("_Emissive", emissiveTexture); } return(material); }
public void CreatePresets() { //ROLLog.debug("TankDimensionGUI: CreatePresets()"); presetScroll = GUILayout.BeginScrollView(presetScroll); //ROLLog.debug("TankDimensionGUI: ForEach through Files."); foreach (string f in files) { file = f; //ROLLog.debug("TankDimensionGUI: Load ConfigNode"); ConfigNode config = ConfigNode.Load(file); // If the player is deleting the files, append the names if (deleteEnabled) { if (GUILayout.Button($"Delete {config.GetValue("name")} [{config.GetValue("diameter")}m]")) { deleteFile = file; deleteFileName = config.GetValue("name"); File.Delete(deleteFile); UpdatePresetList(); } } else { if (GUILayout.Button($"{config.GetValue("name")} [{config.GetValue("diameter")} m]")) { // Set currentDiameter of ROTank diameter = ROLUtils.safeParseFloat(config.GetValue("diameter")); nameString = config.GetValue("name"); diameterString = diameter.ToString("N3"); feet = false; SetCurrentDiameter(diameter); } } } GUILayout.EndScrollView(); //ROLLog.debug("TankDimensionGUI: Ending CreatePresets()"); }
public string[] getLayoutTitles() { return(ROLUtils.getNames(layouts, m => m.title)); }
//TODO clean this up to be easier to read/understand now that it is optimized for cylinder check only public static void findShieldedPartsCylinder(Part basePart, List <Part> shieldedParts, float topY, float bottomY, float topRadius, float bottomRadius) { float height = topY - bottomY; float largestRadius = topRadius > bottomRadius ? topRadius : bottomRadius; Vector3 lookupCenterLocal = new Vector3(0, bottomY + (height * 0.5f), 0); Vector3 lookupTopLocal = new Vector3(0, topY, 0); Vector3 lookupBottomLocal = new Vector3(0, bottomY, 0); Vector3 lookupCenterGlobal = basePart.transform.TransformPoint(lookupCenterLocal); Ray lookupRay = new Ray(lookupBottomLocal, new Vector3(0, 1, 0)); List <Part> partsFound = new List <Part>(); //do a basic sphere check vs the maximal size of the cylinder Collider[] foundColliders = Physics.OverlapSphere(lookupCenterGlobal, height * 1.5f, 1); foreach (Collider col in foundColliders) { Part pt = col.gameObject.GetComponentUpwards <Part>(); if (pt != null && pt != basePart && pt.vessel == basePart.vessel && !partsFound.Contains(pt)) { partsFound.Add(pt); } } Vector3 otherPartCenterLocal; float partYPos; float partYPercent; float partYRadius; float radiusOffset = topRadius - bottomRadius; foreach (Part pt in partsFound) { Vector3 otherPartCenter = pt.partTransform.TransformPoint(PartGeometryUtil.FindBoundsCentroid(pt.GetRendererBounds(), pt.transform)); //check part bounds center point against conic projection of the fairing otherPartCenterLocal = basePart.transform.InverseTransformPoint(otherPartCenter); //check vs top and bottom of the shielded area if (otherPartCenterLocal.y > lookupTopLocal.y || otherPartCenterLocal.y < lookupBottomLocal.y) { continue; } //quick check vs cylinder radius float distFromLine = ROLUtils.distanceFromLine(lookupRay, otherPartCenterLocal); if (distFromLine > largestRadius) { continue; } //more precise check vs radius of the cone at that Y position partYPos = otherPartCenterLocal.y - lookupBottomLocal.y; partYPercent = partYPos / height; partYRadius = partYPercent * radiusOffset; if (distFromLine > (partYRadius + bottomRadius)) { continue; } shieldedParts.Add(pt); //print("Shielding part: " + pt); } }
private void UpdateTankVolume(bool lw) { if (!lw) { float totalVol = noseModule.moduleVolume + coreModule.moduleVolume + mountModule.moduleVolume; SendVolumeChangedEvent(totalVol); return; } float horScale = currentDiameter / coreModule.definition.diameter; float domeLength = currentDiameter / 2; noseEffectiveLength = horScale * noseModule.definition.effectiveLength; mountEffectiveLength = horScale * mountModule.definition.effectiveLength; coreEffectiveLength = currentLength - domeLength; effectiveLength = noseEffectiveLength + mountEffectiveLength + coreEffectiveLength; /* * debug("================================================"); * debug("<color=green>EFFECTIVE LENGTH INFORMATION</color>"); * debug($"horScale: {horScale}, domeLength: {domeLength}"); * debug($"noseEffectiveLength: {noseEffectiveLength}, mountEffectiveLength: {mountEffectiveLength}, coreEffectiveLength: {coreEffectiveLength}"); * debug($"effectiveLength: {effectiveLength}"); * debug("================================================"); */ // Set the minimum length based on domeLength minLength = Math.Max(0.1f, domeLength - (noseEffectiveLength + mountEffectiveLength)); // Update the float controller to reset the proper minimum length this.ROLupdateUIFloatEditControl(nameof(currentLength), minLength, maxLength, diameterLargeStep, diameterSmallStep, diameterSlideStep, true, currentLength); // Set the tank length to be the same size as the minLength if it is currently smaller if (currentLength < minLength) { currentLength = minLength; } // Calculate the new volume // First, get the additional volume from the nose and mounts float noseDiameter = noseModule.definition.shouldInvert(noseModule.definition.orientation) ? noseModule.definition.upperDiameter : noseModule.definition.lowerDiameter; float noseScale = currentDiameter / noseDiameter; noseScale = Mathf.Pow(noseScale, 3); float mountDiameter = mountModule.definition.shouldInvert(mountModule.definition.orientation) ? mountModule.definition.lowerDiameter : mountModule.definition.upperDiameter; float mountScale = currentDiameter / mountDiameter; mountScale = Mathf.Pow(mountScale, 3); noseAdditionalVol = noseScale * noseModule.definition.additionalVolume * 1000f; mountAdditionalVol = mountScale * mountModule.definition.additionalVolume * 1000f; // Calculate the volume of the main tank float r = currentDiameter / 2; effectiveVolume = (ROLUtils.EllipsoidVolume(r, r, r / 2) + ROLUtils.CylinderVolume(r, effectiveLength)) * 1000f; effectiveVolume += noseAdditionalVol + mountAdditionalVol; /* * debug("================================================"); * debug("<color=blue>EFFECTIVE VOLUME INFORMATION</color>"); * debug($"noseScale: {noseScale}, mountScale: {mountScale}"); * debug($"noseAdditionalOrig: {noseModule.definition.additionalVolume}, noseAdditionalVol: {noseAdditionalVol}, coreAdditionalOrig: {mountModule.definition.additionalVolume}, mountAdditionalVol: {mountAdditionalVol}"); * debug($"origEffectiveVolume: {effectiveVolume - noseAdditionalVol - mountAdditionalVol}"); * debug($"effectiveVolume: {effectiveVolume}"); * debug("================================================"); */ ROLModInterop.RealFuelsVolumeUpdate(part, effectiveVolume); }
/// <summary> /// Initialize the UI controls, including default values, and specifying delegates for their 'onClick' methods.<para/> /// All UI based interaction code will be defined/run through these delegates. /// </summary> public void InitializeUI() { // Set up the core variant UI control string[] variantNames = ROLUtils.getNames(variantSets.Values, m => m.variantName); this.ROLupdateUIChooseOptionControl(nameof(currentVariant), variantNames, variantNames, true, currentVariant); Fields[nameof(currentVariant)].guiActiveEditor = variantSets.Count > 1; Fields[nameof(currentVariant)].uiControlEditor.onFieldChanged = (a, b) => { // Query the index from that variant set ModelDefinitionVariantSet prevMdvs = GetVariantSet(coreModule.definition.name); // This is the index of the currently selected model within its variant set int previousIndex = prevMdvs.IndexOf(coreModule.layoutOptions); // Grab ref to the current/new variant set ModelDefinitionVariantSet mdvs = GetVariantSet(currentVariant); // And a reference to the model from same index out of the new set ([] call does validation internally for IAOOBE) ModelDefinitionLayoutOptions newCoreDef = mdvs[previousIndex]; // Now, call model-selected on the core model to update for the changes, including symmetry counterpart updating. this.ROLactionWithSymmetry(m => { m.coreModule.modelSelected(newCoreDef.definition.name); lengthWidth = coreModule.definition.lengthWidth; Fields[nameof(panelLength)].guiActiveEditor = lengthWidth; Fields[nameof(panelWidth)].guiActiveEditor = lengthWidth; Fields[nameof(panelScale)].guiActiveEditor = !lengthWidth; m.ResetModel(); }); ModelChangedHandlerWithSymmetry(true, true); }; Fields[nameof(currentCore)].uiControlEditor.onFieldChanged = (a, b) => { coreModule.modelSelected(a, b); lengthWidth = coreModule.definition.lengthWidth; Fields[nameof(panelLength)].guiActiveEditor = lengthWidth; Fields[nameof(panelWidth)].guiActiveEditor = lengthWidth; Fields[nameof(panelScale)].guiActiveEditor = !lengthWidth; if (!lengthWidth) { this.ROLupdateUIFloatEditControl(nameof(panelScale), 0.1f, 100f, largeStep, smallStep, slideStep, true, panelScale); } else { this.ROLupdateUIFloatEditControl(nameof(panelLength), minLength, maxLength, largeStep, smallStep, slideStep, true, panelLength); this.ROLupdateUIFloatEditControl(nameof(panelWidth), minWidth, maxWidth, largeStep, smallStep, slideStep, true, panelWidth); } ModelChangedHandlerWithSymmetry(true, true); }; Fields[nameof(panelLength)].uiControlEditor.onFieldChanged = Fields[nameof(panelLength)].uiControlEditor.onSymmetryFieldChanged = (a, b) => { if ((float)a.GetValue(this) != prevLength) { ModelChangedHandler(true); prevLength = panelLength; } }; Fields[nameof(panelWidth)].uiControlEditor.onFieldChanged = Fields[nameof(panelWidth)].uiControlEditor.onSymmetryFieldChanged = (a, b) => { if ((float)a.GetValue(this) != prevWidth) { ModelChangedHandler(true); prevWidth = panelWidth; } }; Fields[nameof(panelScale)].uiControlEditor.onFieldChanged = Fields[nameof(panelScale)].uiControlEditor.onSymmetryFieldChanged = (a, b) => { if ((float)a.GetValue(this) != prevScale) { ModelChangedHandler(true); prevScale = panelScale; } }; Fields[nameof(panelScale)].guiActiveEditor = !lengthWidth; if (!lengthWidth) { this.ROLupdateUIFloatEditControl(nameof(panelScale), 0.1f, 100f, largeStep, smallStep, slideStep, true, panelScale); } Fields[nameof(TechLevel)].uiControlEditor.onFieldChanged = (a, b) => { ModelChangedHandlerWithSymmetry(true, true); }; if (maxLength == minLength || !lengthWidth) { Fields[nameof(panelLength)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(panelLength), minLength, maxLength, largeStep, smallStep, slideStep, true, panelLength); } if (maxWidth == minWidth || !lengthWidth) { Fields[nameof(panelWidth)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(panelWidth), minWidth, maxWidth, largeStep, smallStep, slideStep, true, panelWidth); } if (HighLogic.LoadedSceneIsEditor) { GameEvents.onEditorShipModified.Add(OnEditorVesselModified); } }
/// <summary> /// Initialize the UI controls, including default values, and specifying delegates for their 'onClick' methods.<para/> /// All UI based interaction code will be defined/run through these delegates. /// </summary> public void InitializeUI() { //set up the core variant UI control string[] variantNames = ROLUtils.getNames(variantSets.Values, m => m.variantName); this.ROLupdateUIChooseOptionControl(nameof(currentVariant), variantNames, variantNames, true, currentVariant); Fields[nameof(currentVariant)].guiActiveEditor = variantSets.Count > 1; Fields[nameof(currentVariant)].uiControlEditor.onFieldChanged = (a, b) => { //TODO find variant set for the currently enabled core model //query the index from that variant set ModelDefinitionVariantSet prevMdvs = GetVariantSet(coreModule.definition.name); //this is the index of the currently selected model within its variant set int previousIndex = prevMdvs.IndexOf(coreModule.layoutOptions); //grab ref to the current/new variant set ModelDefinitionVariantSet mdvs = GetVariantSet(currentVariant); //and a reference to the model from same index out of the new set ([] call does validation internally for IAOOBE) ModelDefinitionLayoutOptions newCoreDef = mdvs[previousIndex]; //now, call model-selected on the core model to update for the changes, including symmetry counterpart updating. this.ROLactionWithSymmetry(m => { if (lengthWidth) { m.SetModelFromDimensions(); } else { m.coreModule.modelSelected(newCoreDef.definition.name); } }); ModelChangedHandlerWithSymmetry(true, true); }; Fields[nameof(currentDiameter)].uiControlEditor.onFieldChanged = Fields[nameof(currentDiameter)].uiControlEditor.onSymmetryFieldChanged = OnDiameterChanged; Fields[nameof(currentLength)].uiControlEditor.onFieldChanged = Fields[nameof(currentLength)].uiControlEditor.onSymmetryFieldChanged = OnLengthChanged; Fields[nameof(currentVScale)].uiControlEditor.onFieldChanged = (a, b) => { ModelChangedHandlerWithSymmetry(true, true); }; Fields[nameof(currentNose)].uiControlEditor.onFieldChanged = (a, b) => { noseModule.modelSelected(a, b); ModelChangedHandlerWithSymmetry(true, true); }; Fields[nameof(currentCore)].uiControlEditor.onFieldChanged = (a, b) => { coreModule.modelSelected(a, b); ModelChangedHandlerWithSymmetry(true, true); }; Fields[nameof(currentMount)].uiControlEditor.onFieldChanged = (a, b) => { mountModule.modelSelected(a, b); ModelChangedHandlerWithSymmetry(true, true); }; //------------------MODEL DIAMETER / LENGTH SWITCH UI INIT---------------------// if (maxDiameter == minDiameter) { Fields[nameof(currentDiameter)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(currentDiameter), minDiameter, maxDiameter, diameterLargeStep, diameterSmallStep, diameterSlideStep, true, currentDiameter); } if (maxLength == minLength || !lengthWidth) { Fields[nameof(currentLength)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(currentLength), minLength, maxLength, diameterLargeStep, diameterSmallStep, diameterSlideStep, true, currentLength); } Fields[nameof(currentVScale)].guiActiveEditor = enableVScale && !lengthWidth; Events[nameof(ResetModel)].guiActiveEditor = !lengthWidth; //------------------MODULE TEXTURE SWITCH UI INIT---------------------// Fields[nameof(currentNoseTexture)].uiControlEditor.onFieldChanged = noseModule.textureSetSelected; Fields[nameof(currentCoreTexture)].uiControlEditor.onFieldChanged = coreModule.textureSetSelected; Fields[nameof(currentMountTexture)].uiControlEditor.onFieldChanged = mountModule.textureSetSelected; if (HighLogic.LoadedSceneIsEditor) { GameEvents.onEditorShipModified.Add(new EventData <ShipConstruct> .OnEvent(OnEditorVesselModified)); } }
public static void findShieldedPartsCylinder(Part basePart, Bounds fairingRenderBounds, List <Part> shieldedParts, float topY, float bottomY, float topRadius, float bottomRadius) { float height = topY - bottomY; float largestRadius = topRadius > bottomRadius ? topRadius : bottomRadius; Vector3 lookupCenterLocal = new Vector3(0, bottomY + (height * 0.5f), 0); Vector3 lookupTopLocal = new Vector3(0, topY, 0); Vector3 lookupBottomLocal = new Vector3(0, bottomY, 0); Vector3 lookupCenterGlobal = basePart.transform.TransformPoint(lookupCenterLocal); Ray lookupRay = new Ray(lookupBottomLocal, new Vector3(0, 1, 0)); List <Part> partsFound = new List <Part>(); Collider[] foundColliders = Physics.OverlapSphere(lookupCenterGlobal, height * 1.5f, 1); foreach (Collider col in foundColliders) { Part pt = col.gameObject.GetComponentUpwards <Part>(); if (pt != null && pt != basePart && pt.vessel == basePart.vessel && !partsFound.Contains(pt)) { partsFound.Add(pt); } } Bounds[] otherPartBounds; Vector3 otherPartCenterLocal; float partYPos; float partYPercent; float partYRadius; float radiusOffset = topRadius - bottomRadius; foreach (Part pt in partsFound) { //check basic render bounds for containment //TODO this check misses the case where the fairing is long/tall, containing a wide part; it will report that the wide part can fit inside //of the fairing, due to the relative size of their colliders otherPartBounds = pt.GetRendererBounds(); if (PartGeometryUtil.MergeBounds(otherPartBounds, pt.transform).size.sqrMagnitude > fairingRenderBounds.size.sqrMagnitude) { continue; } Vector3 otherPartCenter = pt.partTransform.TransformPoint(PartGeometryUtil.FindBoundsCentroid(otherPartBounds, pt.transform)); if (!fairingRenderBounds.Contains(otherPartCenter)) { continue; } //check part bounds center point against conic projection of the fairing otherPartCenterLocal = basePart.transform.InverseTransformPoint(otherPartCenter); //check vs top and bottom of the shielded area if (otherPartCenterLocal.y > lookupTopLocal.y || otherPartCenterLocal.y < lookupBottomLocal.y) { continue; } //quick check vs cylinder radius float distFromLine = ROLUtils.distanceFromLine(lookupRay, otherPartCenterLocal); if (distFromLine > largestRadius) { continue; } //more precise check vs radius of the cone at that Y position partYPos = otherPartCenterLocal.y - lookupBottomLocal.y; partYPercent = partYPos / height; partYRadius = partYPercent * radiusOffset; if (distFromLine > (partYRadius + bottomRadius)) { continue; } shieldedParts.Add(pt); } }
public void EnableColliders(bool val) { ROLUtils.enableColliderRecursive(rootObject.transform, val); }
/// <summary> /// Initialization method. Sets up model modules, loads their configs from the input config node. Does all initial linking of part-modules.<para/> /// Does NOT set up their UI interaction -- that is all handled during OnStart() /// </summary> private void Initialize() { if (initialized) { return; } ROLLog.debug($"{modName}: Initialize Starting"); initialized = true; prevLength = panelLength; prevWidth = panelWidth; prevScale = panelScale; ROLLog.debug($"{modName}: Initialize() parseCSV"); coreNodeNames = ROLUtils.parseCSV(coreManagedNodes); ROLLog.debug($"{modName}: Initialize() Model-Module Initialization"); // Model-Module Setup / Initialization ConfigNode node = ROLConfigNodeUtils.parseConfigNode(configNodeData); ROLLog.debug($"{modName}: Initialize() Core Model Nodes"); // List of CORE model nodes from config // each one may contain multiple 'model=modelDefinitionName' entries // but must contain no more than a single 'variant' entry. // If no variant is specified, they are added to the 'Default' variant. ConfigNode[] coreDefNodes = node.GetNodes("CORE"); ROLLog.debug($"{modName}: Initialize() MDLO"); List <ModelDefinitionLayoutOptions> coreDefList = new List <ModelDefinitionLayoutOptions>(); int coreDefLen = coreDefNodes.Length; for (int i = 0; i < coreDefLen; i++) { string variantName = coreDefNodes[i].ROLGetStringValue("variant", "Default"); coreDefs = ROLModelData.getModelDefinitionLayouts(coreDefNodes[i].ROLGetStringValues("model")); coreDefList.AddUniqueRange(coreDefs); ModelDefinitionVariantSet mdvs = getVariantSet(variantName); mdvs.addModels(coreDefs); } coreDefs = coreDefList.ToArray(); coreModule = new ROLModelModule <ModuleROSolar>(part, this, getRootTransform("ModuleROSolar-CORE"), ModelOrientation.CENTRAL, nameof(currentCore), null, null, null); coreModule.name = "ModuleROSolar-Core"; coreModule.getSymmetryModule = m => m.coreModule; coreModule.getValidOptions = () => getVariantSet(currentVariant).definitions; coreModule.setupModelList(coreDefs); coreModule.setupModel(); if (GameDatabase.Instance.GetConfigNode("ROSolar/TechLimits/ROSOLAR_CONFIG") is ConfigNode ROSconfig) { SolarTechLimit.Init(ROSconfig); } stl = SolarTechLimit.GetTechLevel(techLevel); UpdateModulePositions(); UpdateAttachNodes(false); UpdateAvailableVariants(); UpdateMassAndCost(); RecalculateStats(); ROLStockInterop.updatePartHighlighting(part); }
/// <summary> /// Initialization method. Sets up model modules, loads their configs from the input config node. Does all initial linking of part-modules.<para/> /// Does NOT set up their UI interaction -- that is all handled during OnStart() /// </summary> private void initialize() { if (initialized) { return; } initialized = true; prevDiameter = currentDiameter; if (lengthWidth) { prevLength = currentLength; } noseNodeNames = ROLUtils.parseCSV(noseManagedNodes); coreNodeNames = ROLUtils.parseCSV(coreManagedNodes); mountNodeNames = ROLUtils.parseCSV(mountManagedNodes); //model-module setup/initialization ConfigNode node = ROLConfigNodeUtils.parseConfigNode(configNodeData); //list of CORE model nodes from config //each one may contain multiple 'model=modelDefinitionName' entries //but must contain no more than a single 'variant' entry. //if no variant is specified, they are added to the 'Default' variant. ConfigNode[] coreDefNodes = node.GetNodes("CORE"); List <ModelDefinitionLayoutOptions> coreDefList = new List <ModelDefinitionLayoutOptions>(); int coreDefLen = coreDefNodes.Length; for (int i = 0; i < coreDefLen; i++) { string variantName = coreDefNodes[i].ROLGetStringValue("variant", "Default"); coreDefs = ROLModelData.getModelDefinitionLayouts(coreDefNodes[i].ROLGetStringValues("model")); coreDefList.AddUniqueRange(coreDefs); ModelDefinitionVariantSet mdvs = getVariantSet(variantName); mdvs.addModels(coreDefs); } coreDefs = coreDefList.ToArray(); //model defs - brought here so we can capture the array rather than the config node+method call noseDefs = ROLModelData.getModelDefinitions(node.GetNodes("NOSE")); mountDefs = ROLModelData.getModelDefinitions(node.GetNodes("MOUNT")); noseModule = new ROLModelModule <ModuleROTank>(part, this, getRootTransform("ModularPart-NOSE"), ModelOrientation.TOP, nameof(currentNose), null, nameof(currentNoseTexture), nameof(noseModulePersistentData)); noseModule.name = "ModuleROTank-Nose"; noseModule.getSymmetryModule = m => m.noseModule; noseModule.getValidOptions = () => noseDefs; coreModule = new ROLModelModule <ModuleROTank>(part, this, getRootTransform("ModularPart-CORE"), ModelOrientation.CENTRAL, nameof(currentCore), null, nameof(currentCoreTexture), nameof(coreModulePersistentData)); coreModule.name = "ModuleROTank-Core"; coreModule.getSymmetryModule = m => m.coreModule; coreModule.getValidOptions = () => getVariantSet(currentVariant).definitions; mountModule = new ROLModelModule <ModuleROTank>(part, this, getRootTransform("ModularPart-MOUNT"), ModelOrientation.BOTTOM, nameof(currentMount), null, nameof(currentMountTexture), nameof(mountModulePersistentData)); mountModule.name = "ModuleROTank-Mount"; mountModule.getSymmetryModule = m => m.mountModule; mountModule.getValidOptions = () => mountDefs; noseModule.volumeScalar = volumeScalingPower; coreModule.volumeScalar = volumeScalingPower; mountModule.volumeScalar = volumeScalingPower; //set up the model lists and load the currently selected model noseModule.setupModelList(noseDefs); coreModule.setupModelList(coreDefs); mountModule.setupModelList(mountDefs); coreModule.setupModel(); noseModule.setupModel(); mountModule.setupModel(); updateModulePositions(); updateDimensions(); updateAttachNodes(false); updateAvailableVariants(); if (scaleMass) { updateMass(); } if (scaleCost) { updateCost(); } ROLStockInterop.updatePartHighlighting(part); UpdateTankVolume(lengthWidth); }
//creates/recreates FairingData instances from data from config node and any persistent node (if applicable) private void LoadFairingData(ConfigNode node) { recolorHandler = new RecoloringHandler(Fields[nameof(customColorData)]); ConfigNode[] fairingNodes = node.GetNodes("FAIRING"); fairingParts = new ROLNodeFairingData[fairingNodes.Length]; Transform modelBase = part.transform.FindRecursive("model"); Transform parent; ModuleROLNodeFairing[] cs = part.GetComponents <ModuleROLNodeFairing>(); int l = Array.IndexOf(cs, this); int moduleIndex = l; for (int i = 0; i < fairingNodes.Length; i++) { parent = modelBase.FindOrCreate(fairingName + "-" + moduleIndex + "-" + i); fairingParts[i] = new ROLNodeFairingData(); fairingParts[i].load(fairingNodes[i], parent.gameObject); if (fairingParts[i].canAdjustTop) { enableTopDiameterControls = true; if (guiTopDiameter < 0) { guiTopDiameter = fairingParts[i].topRadius * 2f; } else { fairingParts[i].topRadius = guiTopDiameter * 0.5f; } } if (fairingParts[i].canAdjustBottom) { enableBottomDiameterControls = true; if (guiBottomDiameter < 0) { guiBottomDiameter = fairingParts[i].bottomRadius * 2f; } else { fairingParts[i].bottomRadius = guiBottomDiameter * 0.5f; } } } //reload fairing data from persistence; //it -should- already match the guiTopDiameter/guiBottomDiameter (or else was already corrupted/invalid when saved out). if (!String.IsNullOrEmpty(persistentDataString)) { String[] datas = ROLUtils.parseCSV(persistentDataString, ":"); int length = datas.Length; for (int i = 0; i < length; i++) { fairingParts[i].LoadPersistence(datas[i]); } } string[] names = node.ROLGetStringValues("textureSet"); string[] titles = ROLUtils.getNames(TexturesUnlimitedLoader.getTextureSets(names), m => m.title); TextureSet t = TexturesUnlimitedLoader.getTextureSet(currentTextureSet); if (t == null) { currentTextureSet = names[0]; t = TexturesUnlimitedLoader.getTextureSet(currentTextureSet); initializedColors = false; } if (!initializedColors) { initializedColors = true; recolorHandler.setColorData(t.maskColors); } this.updateUIChooseOptionControl(nameof(currentTextureSet), names, titles, true, currentTextureSet); }
/// <summary> /// Initialize the UI controls, including default values, and specifying delegates for their 'onClick' methods.<para/> /// All UI based interaction code will be defined/run through these delegates. /// </summary> public void initializeUI() { Action <ModuleROTank> modelChangedAction = (m) => { m.updateModulePositions(); m.updateDimensions(); m.updateAttachNodes(true); m.updateAvailableVariants(); m.updateDragCubes(); if (scaleMass) { m.updateMass(); } if (scaleCost) { m.updateCost(); } //ROLModInterop.updateResourceVolume(m.part); m.UpdateTankVolume(lengthWidth); }; //set up the core variant UI control string[] variantNames = ROLUtils.getNames(variantSets.Values, m => m.variantName); this.ROLupdateUIChooseOptionControl(nameof(currentVariant), variantNames, variantNames, true, currentVariant); Fields[nameof(currentVariant)].guiActiveEditor = variantSets.Count > 1; Fields[nameof(currentVariant)].uiControlEditor.onFieldChanged = (a, b) => { //TODO find variant set for the currently enabled core model //query the index from that variant set ModelDefinitionVariantSet prevMdvs = getVariantSet(coreModule.definition.name); //this is the index of the currently selected model within its variant set int previousIndex = prevMdvs.indexOf(coreModule.layoutOptions); //grab ref to the current/new variant set ModelDefinitionVariantSet mdvs = getVariantSet(currentVariant); //and a reference to the model from same index out of the new set ([] call does validation internally for IAOOBE) ModelDefinitionLayoutOptions newCoreDef = mdvs[previousIndex]; //now, call model-selected on the core model to update for the changes, including symmetry counterpart updating. this.ROLactionWithSymmetry(m => { m.currentVariant = currentVariant; if (lengthWidth) { m.SetModelFromDimensions(); } else { m.coreModule.modelSelected(newCoreDef.definition.name); } modelChangedAction(m); }); }; Fields[nameof(currentDiameter)].uiControlEditor.onFieldChanged = (a, b) => { this.ROLactionWithSymmetry(m => { if (m != this) { m.currentDiameter = this.currentDiameter; } if (lengthWidth) { m.SetModelFromDimensions(); } modelChangedAction(m); m.prevDiameter = m.currentDiameter; }); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(currentLength)].uiControlEditor.onFieldChanged = (a, b) => { this.ROLactionWithSymmetry(m => { if (m != this) { m.currentLength = this.currentLength; } m.SetModelFromDimensions(); modelChangedAction(m); m.prevLength = m.currentLength; }); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(currentVScale)].uiControlEditor.onFieldChanged = (a, b) => { this.ROLactionWithSymmetry(m => { if (m != this) { m.currentVScale = this.currentVScale; } modelChangedAction(m); }); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(currentNose)].uiControlEditor.onFieldChanged = (a, b) => { noseModule.modelSelected(a, b); this.ROLactionWithSymmetry(modelChangedAction); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(currentCore)].uiControlEditor.onFieldChanged = (a, b) => { coreModule.modelSelected(a, b); this.ROLactionWithSymmetry(modelChangedAction); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(currentMount)].uiControlEditor.onFieldChanged = (a, b) => { mountModule.modelSelected(a, b); this.ROLactionWithSymmetry(modelChangedAction); ROLStockInterop.fireEditorUpdate(); }; //------------------MODEL DIAMETER / LENGTH SWITCH UI INIT---------------------// if (maxDiameter == minDiameter) { Fields[nameof(currentDiameter)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(currentDiameter), minDiameter, maxDiameter, diameterLargeStep, diameterSmallStep, diameterSlideStep, true, currentDiameter); } if (maxLength == minLength || !lengthWidth) { Fields[nameof(currentLength)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(currentLength), minLength, maxLength, diameterLargeStep, diameterSmallStep, diameterSlideStep, true, currentLength); } if (!lengthWidth) { Fields[nameof(currentVScale)].guiActiveEditor = enableVScale; Events[nameof(ResetModel)].guiActiveEditor = true; } if (lengthWidth) { Fields[nameof(currentVScale)].guiActiveEditor = false; Events[nameof(ResetModel)].guiActiveEditor = false; } //------------------MODULE TEXTURE SWITCH UI INIT---------------------// Fields[nameof(currentNoseTexture)].uiControlEditor.onFieldChanged = noseModule.textureSetSelected; Fields[nameof(currentCoreTexture)].uiControlEditor.onFieldChanged = coreModule.textureSetSelected; Fields[nameof(currentMountTexture)].uiControlEditor.onFieldChanged = mountModule.textureSetSelected; if (HighLogic.LoadedSceneIsEditor) { GameEvents.onEditorShipModified.Add(new EventData <ShipConstruct> .OnEvent(onEditorVesselModified)); } }
public static Vector3 ROLGetVector3(this ConfigNode node, string name, Vector3 defaultValue) { return(node.GetValue(name) is string value && value.Split(',') is string[] vals && vals.Length >= 3 ? new Vector3((float)ROLUtils.safeParseDouble(vals[0]), (float)ROLUtils.safeParseDouble(vals[1]), (float)ROLUtils.safeParseDouble(vals[2])) : defaultValue); }
/// <summary> /// Initialize the UI controls, including default values, and specifying delegates for their 'onClick' methods.<para/> /// All UI based interaction code will be defined/run through these delegates. /// </summary> public void InitializeUI() { ROLLog.debug($"ModuleDeployablePart.deployState: {this.deployState}"); ROLLog.debug($"{modName}InitalizeUI() modelChangedAction"); Action <ModuleROSolar> modelChangedAction = (m) => { m.stl = SolarTechLimit.GetTechLevel(techLevel); m.UpdateModulePositions(); m.UpdateAttachNodes(true); m.UpdateAvailableVariants(); m.UpdateDragCubes(); m.UpdateMassAndCost(); m.RecalculateStats(); }; // Set up the core variant UI control string[] variantNames = ROLUtils.getNames(variantSets.Values, m => m.variantName); this.ROLupdateUIChooseOptionControl(nameof(currentVariant), variantNames, variantNames, true, currentVariant); Fields[nameof(currentVariant)].guiActiveEditor = variantSets.Count > 1; Fields[nameof(currentVariant)].uiControlEditor.onFieldChanged = (a, b) => { // Query the index from that variant set ModelDefinitionVariantSet prevMdvs = getVariantSet(coreModule.definition.name); // This is the index of the currently selected model within its variant set int previousIndex = prevMdvs.indexOf(coreModule.layoutOptions); // Grab ref to the current/new variant set ModelDefinitionVariantSet mdvs = getVariantSet(currentVariant); // And a reference to the model from same index out of the new set ([] call does validation internally for IAOOBE) ModelDefinitionLayoutOptions newCoreDef = mdvs[previousIndex]; // Now, call model-selected on the core model to update for the changes, including symmetry counterpart updating. this.ROLactionWithSymmetry(m => { m.currentVariant = currentVariant; m.coreModule.modelSelected(newCoreDef.definition.name); lengthWidth = coreModule.definition.lengthWidth; if (!lengthWidth) { Fields[nameof(panelLength)].guiActiveEditor = false; Fields[nameof(panelWidth)].guiActiveEditor = false; Fields[nameof(panelScale)].guiActiveEditor = true; this.ROLupdateUIFloatEditControl(nameof(panelScale), 0.1f, 100f, largeStep, smallStep, slideStep, true, panelScale); } else { Fields[nameof(panelLength)].guiActiveEditor = true; Fields[nameof(panelWidth)].guiActiveEditor = true; Fields[nameof(panelScale)].guiActiveEditor = false; this.ROLupdateUIFloatEditControl(nameof(panelLength), minLength, maxLength, largeStep, smallStep, slideStep, true, panelLength); this.ROLupdateUIFloatEditControl(nameof(panelWidth), minWidth, maxWidth, largeStep, smallStep, slideStep, true, panelWidth); } m.ResetModel(); modelChangedAction(m); }); }; Fields[nameof(currentCore)].uiControlEditor.onFieldChanged = (a, b) => { coreModule.modelSelected(a, b); lengthWidth = coreModule.definition.lengthWidth; if (!lengthWidth) { Fields[nameof(panelLength)].guiActiveEditor = false; Fields[nameof(panelWidth)].guiActiveEditor = false; Fields[nameof(panelScale)].guiActiveEditor = true; this.ROLupdateUIFloatEditControl(nameof(panelScale), 0.1f, 100f, largeStep, smallStep, slideStep, true, panelScale); } else { Fields[nameof(panelLength)].guiActiveEditor = true; Fields[nameof(panelWidth)].guiActiveEditor = true; Fields[nameof(panelScale)].guiActiveEditor = false; this.ROLupdateUIFloatEditControl(nameof(panelLength), minLength, maxLength, largeStep, smallStep, slideStep, true, panelLength); this.ROLupdateUIFloatEditControl(nameof(panelWidth), minWidth, maxWidth, largeStep, smallStep, slideStep, true, panelWidth); } this.ROLactionWithSymmetry(modelChangedAction); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(panelLength)].uiControlEditor.onFieldChanged = (a, b) => { this.ROLactionWithSymmetry(m => { if (m != this) { m.panelLength = this.panelLength; } modelChangedAction(m); m.prevLength = m.panelLength; }); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(panelWidth)].uiControlEditor.onFieldChanged = (a, b) => { this.ROLactionWithSymmetry(m => { if (m != this) { m.panelWidth = this.panelWidth; } modelChangedAction(m); m.prevWidth = m.panelWidth; }); ROLStockInterop.fireEditorUpdate(); }; Fields[nameof(panelScale)].uiControlEditor.onFieldChanged = (a, b) => { this.ROLactionWithSymmetry(m => { if (m != this) { m.panelScale = this.panelScale; } modelChangedAction(m); m.prevScale = m.panelScale; }); ROLStockInterop.fireEditorUpdate(); }; if (maxLength == minLength || !lengthWidth) { Fields[nameof(panelLength)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(panelLength), minLength, maxLength, largeStep, smallStep, slideStep, true, panelLength); } if (maxWidth == minWidth || !lengthWidth) { Fields[nameof(panelWidth)].guiActiveEditor = false; } else { this.ROLupdateUIFloatEditControl(nameof(panelWidth), minWidth, maxWidth, largeStep, smallStep, slideStep, true, panelWidth); } if (lengthWidth) { Fields[nameof(panelScale)].guiActiveEditor = false; } else { Fields[nameof(panelScale)].guiActiveEditor = true; this.ROLupdateUIFloatEditControl(nameof(panelScale), 0.1f, 100f, largeStep, smallStep, slideStep, true, panelScale); } if (HighLogic.LoadedSceneIsEditor) { GameEvents.onEditorShipModified.Add(new EventData <ShipConstruct> .OnEvent(onEditorVesselModified)); } Fields[nameof(TechLevel)].uiControlEditor.onFieldChanged = (a, b) => { UpdateMassAndCost(); RecalculateStats(); this.ROLactionWithSymmetry(modelChangedAction); ROLStockInterop.fireEditorUpdate(); }; }
/// <summary> /// Returns a string array of the UI-label titles for the texture sets for this model definition.<para/> /// Returned in the same order as getTextureSetNames(), so they can be used in with basic indexing to map one value to another. /// </summary> /// <returns></returns> public string[] GetTextureSetTitles() => ROLUtils.getNames(textureSets, m => m.title);