Inheritance: CFGUtilPartModule, IPartMassModifier, IPartCostModifier, IModuleInfo
        private static DialogGUIBase[] CreateOptions(ModuleB9PartSwitch module, IList <Callback> afterCreateCallbacks)
        {
            List <DialogGUIBase> options = new List <DialogGUIBase>();

            SwitcherSubtypeDescriptionGenerator subtypeDescriptionGenerator = new SwitcherSubtypeDescriptionGenerator(module);

            foreach (PartSubtype subtype in module.subtypes)
            {
                if (!subtype.IsUnlocked())
                {
                    continue;
                }

                if (subtype == module.CurrentSubtype)
                {
                    string         currentSubtypeText = Localization.PartSwitchFlightDialog_CurrentSubtypeLabel(subtype.title); // <<1>> (Current)
                    DialogGUILabel label = new DialogGUILabel(currentSubtypeText, HighLogic.UISkin.button);
                    afterCreateCallbacks.Add(delegate
                    {
                        if (!(label.uiItem.GetComponent <TextMeshProUGUI>() is TextMeshProUGUI textUI))
                        {
                            throw new Exception("Could not find TextMeshProUGUI");
                        }
                        else
                        {
                            textUI.raycastTarget = true;
                        }
                    });
Beispiel #2
0
 public static void Spawn(ModuleB9PartSwitch module)
 {
     if (!module.subtypes.Any(subtype => subtype != module.CurrentSubtype && subtype.allowSwitchInFlight))
     {
         return;
     }
     MaybeCreateResourceRemovalWarning(module, () => CreateDialogue(module));
 }
Beispiel #3
0
 private bool DisplayInfoOnSwitcher(ModuleB9PartSwitch switcher)
 {
     return
         (switcher.ChangesMass ||
          switcher.ChangesCost ||
          switcher.PartFieldManaged(SubtypePartFields.MaxTemp) ||
          switcher.PartFieldManaged(SubtypePartFields.SkinMaxTemp) ||
          switcher.PartFieldManaged(SubtypePartFields.CrashTolerance));
 }
Beispiel #4
0
 private bool DisplayInfoOnSwitcher(ModuleB9PartSwitch switcher)
 {
     return
         (switcher.ChangesMass ||
          switcher.ChangesCost ||
          switcher.HasPartAspectLock(PartMaxTempModifier.PART_ASPECT_LOCK) ||
          switcher.HasPartAspectLock(PartSkinMaxTempModifier.PART_ASPECT_LOCK) ||
          switcher.HasPartAspectLock(PartCrashToleranceModifier.PART_ASPECT_LOCK));
 }
 private bool DisplayInfoOnSwitcher(ModuleB9PartSwitch switcher)
 {
     return
         switcher.ChangesMass ||
         switcher.ChangesCost ||
         switcher.PartFieldManaged(SubtypePartFields.MaxTemp) ||
         switcher.PartFieldManaged(SubtypePartFields.SkinMaxTemp) ||
         switcher.PartFieldManaged(SubtypePartFields.CrashTolerance);
 }
 public static void MaybeCreateResourceRemovalWarning(ModuleB9PartSwitch module, Action onConfirm)
 {
     if (HighLogic.LoadedSceneIsFlight && module.CurrentTankType.ResourceNames.Any(name => module.part.Resources[name].amount > 0))
     {
         CreateWarning(module, onConfirm);
     }
     else
     {
         onConfirm();
     }
 }
 public static void Spawn(ModuleB9PartSwitch module)
 {
     try
     {
         MaybeCreateResourceRemovalWarning(module, () => CreateDialogue(module));
     }
     catch (Exception ex)
     {
         UnityEngine.Debug.LogException(ex);
         FatalErrorHandler.HandleFatalError(ex);
     }
 }
 public static void Spawn(ModuleB9PartSwitch module)
 {
     try
     {
         MaybeCreateResourceRemovalWarning(module, () => CreateDialogue(module));
     }
     catch (Exception ex)
     {
         Log.error(ex, ex.Message);
         FatalErrorHandler.HandleFatalError(ex);
     }
 }
        public static void Spawn(ModuleB9PartSwitch module)
        {
            bool showWarning = HighLogic.LoadedSceneIsFlight && module.CurrentTankType.ResourceNames.Any(name => module.part.Resources[name].amount > 0);

            if (showWarning)
            {
                CreateWarning(module);
            }
            else
            {
                CreateDialogue(module);
            }
        }
 private static void CreateDialogue(ModuleB9PartSwitch module)
 {
     PopupDialog.SpawnPopupDialog(
         new MultiOptionDialog(
             "B9PartSwitch_SwitchInFlight",
             Localizer.Format(Localization.PartSwitchFlightDialog_SelectNewSubtypeDialogTitle, module.switcherDescription), // Select <<1>>
             module.part.partInfo.title,
             HighLogic.UISkin,
             CreateOptions(module)
             ),
         false,
         HighLogic.UISkin
         );
 }
        public void SetParent(ModuleB9PartSwitch parent)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent cannot be null");
            }
            if (parent.part == null)
            {
                throw new ArgumentNullException("parent.part cannot be null");
            }

            this.parent = parent;
            part        = parent.part;
        }
 private static void CreateWarning(ModuleB9PartSwitch module)
 {
     PopupDialog.SpawnPopupDialog(
         new MultiOptionDialog(
             "B9PartSwitch_SwitchInFlightWarning",
             Localizer.Format(Localization.PartSwitchFlightDialog_ResourcesWillBeDumpedWarning, module.part.partInfo.title, module.switcherDescription), // <<1>> has resources that will be dumped by switching the <<2>>
             Localization.PartSwitchFlightDialog_ConfirmResourceRemovalDialogTitle,                                                                      // Confirm Resource Removal
             HighLogic.UISkin,
             new DialogGUIButton(Localization.PartSwitchFlightDialog_AcceptString, () => CreateDialogue(module)),
             new DialogGUIButton(Localization.PartSwitchFlightDialog_CancelString, delegate { })
             ),
         false,
         HighLogic.UISkin
         );
 }
        public void Setup(ModuleB9PartSwitch parent)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent cannot be null");
            }
            if (parent.part == null)
            {
                throw new ArgumentNullException("parent.part cannot be null");
            }

            this.parent = parent;

            FindObjects();
            FindNodes();
        }
        private static DialogGUIBase[] CreateOptions(ModuleB9PartSwitch module)
        {
            List <DialogGUIBase> options = new List <DialogGUIBase>();

            foreach (PartSubtype subtype in module.subtypes)
            {
                if (subtype == module.CurrentSubtype)
                {
                    options.Add(new DialogGUILabel(Localizer.Format(Localization.PartSwitchFlightDialog_CurrentSubtypeLabel, subtype.title), HighLogic.UISkin.button)); // <<1>> (Current)
                }
                else if (HighLogic.LoadedSceneIsEditor || subtype.allowSwitchInFlight)
                {
                    options.Add(new DialogGUIButton(subtype.title, () => module.SwitchSubtype(subtype.Name)));
                }
            }

            options.Add(new DialogGUIButton(Localization.PartSwitchFlightDialog_CancelString, delegate { }));

            return(options.ToArray());
        }
        private static void CreateDialogue(ModuleB9PartSwitch module)
        {
            List <Callback> afterCreateCallbacks = new List <Callback>();

            PopupDialog.SpawnPopupDialog(
                new MultiOptionDialog(
                    "B9PartSwitch_SwitchInFlight",
                    Localization.PartSwitchFlightDialog_SelectNewSubtypeDialogTitle(module.switcherDescription), // Select <<1>>
                    module.part.partInfo.title,
                    HighLogic.UISkin,
                    CreateOptions(module, afterCreateCallbacks)
                    ),
                false,
                HighLogic.UISkin
                );

            foreach (Callback callback in afterCreateCallbacks)
            {
                callback();
            }
        }
Beispiel #16
0
        public void Setup(ModuleB9PartSwitch parent, bool displayWarnings = true)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent cannot be null");
            }
            if (parent.part == null)
            {
                throw new ArgumentNullException("parent.part cannot be null");
            }

            this.parent = parent;

            aspectLocks.Clear();

            Part part       = parent.part;
            Part partPrefab = part.GetPrefab() ?? part;

            partModifiers.Clear();

            IEnumerable <object> aspectLocksOnOtherModules = parent.PartAspectLocksOnOtherModules;

            string errorString = null;

            void OnInitializationError(string message)
            {
                LogError(message);

                if (displayWarnings)
                {
                    if (errorString == null)
                    {
                        errorString = $"Initialization errors on {parent} subtype '{Name}'";
                    }

                    errorString += "\n  " + message;
                }
            }

            void MaybeAddModifier(IPartModifier modifier)
            {
                if (modifier == null)
                {
                    return;
                }

                if (modifier is IPartAspectLock partAspectLockHolder)
                {
                    object partAspectLock = partAspectLockHolder;
                    if (aspectLocksOnOtherModules.Contains(partAspectLock))
                    {
                        OnInitializationError($"More than one module can't manage {modifier.Description}");
                        return;
                    }
                    else
                    {
                        aspectLocks.Add(partAspectLock);
                    }
                }

                partModifiers.Add(modifier);
            }

            if (maxTemp > 0)
            {
                MaybeAddModifier(new PartMaxTempModifier(part, partPrefab.maxTemp, maxTemp));
            }

            if (skinMaxTemp > 0)
            {
                MaybeAddModifier(new PartSkinMaxTempModifier(part, partPrefab.skinMaxTemp, skinMaxTemp));
            }

            if (crashTolerance > 0)
            {
                MaybeAddModifier(new PartCrashToleranceModifier(part, partPrefab.crashTolerance, crashTolerance));
            }

            if (attachNode.IsNotNull())
            {
                if (part.attachRules.srfAttach)
                {
                    if (part.srfAttachNode.IsNotNull())
                    {
                        MaybeAddModifier(new PartAttachNodeModifier(part.srfAttachNode, partPrefab.srfAttachNode, attachNode, parent));
                    }
                    else
                    {
                        OnInitializationError("attachNode specified but part does not have a surface attach node");
                    }
                }
                else
                {
                    OnInitializationError("attachNode specified but part does not allow surface attach");
                }
            }

            if (CoMOffset.IsFinite())
            {
                MaybeAddModifier(new PartCoMOffsetModifier(part, partPrefab.CoMOffset, CoMOffset));
            }

            if (CoPOffset.IsFinite())
            {
                MaybeAddModifier(new PartCoPOffsetModifier(part, partPrefab.CoPOffset, CoPOffset));
            }

            if (CoLOffset.IsFinite())
            {
                MaybeAddModifier(new PartCoLOffsetModifier(part, partPrefab.CoLOffset, CoLOffset));
            }

            if (CenterOfBuoyancy.IsFinite())
            {
                MaybeAddModifier(new PartCenterOfBuoyancyModifier(part, partPrefab.CenterOfBuoyancy, CenterOfBuoyancy));
            }

            if (CenterOfDisplacement.IsFinite())
            {
                MaybeAddModifier(new PartCenterOfDisplacementModifier(part, partPrefab.CenterOfDisplacement, CenterOfDisplacement));
            }

            if (stackSymmetry >= 0)
            {
                MaybeAddModifier(new PartStackSymmetryModifier(part, partPrefab.stackSymmetry, stackSymmetry));
            }

            foreach (AttachNodeModifierInfo info in attachNodeModifierInfos)
            {
                foreach (IPartModifier partModifier in info.CreatePartModifiers(part, parent, OnInitializationError))
                {
                    MaybeAddModifier(partModifier);
                }
            }

            foreach (TextureSwitchInfo info in textureSwitches)
            {
                foreach (TextureReplacement replacement in info.CreateTextureReplacements(part, OnInitializationError))
                {
                    MaybeAddModifier(replacement);
                }
            }

            foreach (MaterialModifierInfo materialModifierInfo in materialModifierInfos)
            {
                foreach (IPartModifier partModifier in materialModifierInfo.CreateModifiers(part.GetModelRoot(), OnInitializationError))
                {
                    MaybeAddModifier(partModifier);
                }
            }

            nodes.Clear();
            foreach (IStringMatcher nodeName in nodeNames)
            {
                bool foundNode = false;

                foreach (AttachNode node in part.attachNodes)
                {
                    if (!nodeName.Match(node.id))
                    {
                        continue;
                    }

                    foundNode = true;

                    if (node.nodeType != AttachNode.NodeType.Stack)
                    {
                        OnInitializationError($"Node {node.id} is not a stack node, and thus cannot be managed by ModuleB9PartSwitch");
                        continue;
                    }

                    nodes.Add(node);
                    partModifiers.Add(new AttachNodeToggler(node));
                }

                if (!foundNode)
                {
                    OnInitializationError($"No attach nodes matching '{nodeName}' found");
                }
            }

            if (HasTank)
            {
                foreach (TankResource resource in tankType)
                {
                    float            filledProportion = (resource.percentFilled ?? percentFilled ?? tankType.percentFilled ?? 100f) * 0.01f;
                    bool?            tweakable        = resourcesTweakable ?? tankType.resourcesTweakable;
                    ResourceModifier resourceModifier = new ResourceModifier(resource, () => parent.GetTotalVolume(this), part, filledProportion, tweakable);
                    MaybeAddModifier(resourceModifier);
                }
            }

            transforms.Clear();
            foreach (var transformName in transformNames)
            {
                bool foundTransform = false;

                foreach (Transform transform in part.GetModelRoot().TraverseHierarchy().Where(t => transformName.Match(t.name)))
                {
                    foundTransform = true;
                    partModifiers.Add(new TransformToggler(transform, part));
                    transforms.Add(transform);
                }

                if (!foundTransform)
                {
                    OnInitializationError($"No transforms matching '{transformName}' found");
                }
            }

            foreach (TransformModifierInfo transformModifierInfo in transformModifierInfos)
            {
                foreach (IPartModifier partModifier in transformModifierInfo.CreatePartModifiers(part, OnInitializationError))
                {
                    MaybeAddModifier(partModifier);
                }
            }

            // Icon setup doesn't set partInfo correctly, so it exists but as a copy without partConfig
            if ((part.partInfo?.partConfig).IsNotNull())
            {
                foreach (ModuleModifierInfo moduleModifierInfo in moduleModifierInfos)
                {
                    try
                    {
                        foreach (IPartModifier partModifier in moduleModifierInfo.CreatePartModifiers(part, parent, parent.CreateModuleDataChangedEventDetails()))
                        {
                            MaybeAddModifier(partModifier);
                        }
                    }
                    catch (Exception ex)
                    {
                        OnInitializationError(ex.Message);
                        Debug.LogException(ex);
                    }
                }
            }

            if (!parent.subtypes.Any(subtype => subtype.Name == mirrorSymmetrySubtype))
            {
                OnInitializationError($"Cannot find subtype '{mirrorSymmetrySubtype}' for mirror symmetry subtype");
                mirrorSymmetrySubtype = Name;
            }

            if (errorString.IsNotNull())
            {
                SeriousWarningHandler.DisplaySeriousWarning(errorString);
            }
        }
Beispiel #17
0
        // This runs after OnStart() so everything should be initalized
        public void Start()
        {
            // Check for incompatible modules
            bool modifiedSetup = false;

            List <ModuleB9PartSwitch> otherModules = part.FindModulesImplementing <ModuleB9PartSwitch>();

            for (int i = 0; i < otherModules.Count; i++)
            {
                ModuleB9PartSwitch otherModule = otherModules[i];
                if (otherModule == this)
                {
                    continue;
                }
                bool destroy = false;
                for (int j = 0; j < managedResourceNames.Count; j++)
                {
                    if (otherModule.IsManagedResource(managedResourceNames[j]))
                    {
                        LogError("Two ModuleB9PartSwitch modules cannot manage the same resource: " + managedResourceNames[j]);
                        destroy = true;
                    }
                }
                for (int j = 0; j < managedTransformNames.Count; j++)
                {
                    if (otherModule.IsManagedTransform(managedTransformNames[j]))
                    {
                        LogError("Two ModuleB9PartSwitch modules cannot manage the same transform: " + managedTransformNames[j]);
                        destroy = true;
                    }
                }
                for (int j = 0; j < managedStackNodeIDs.Count; j++)
                {
                    if (otherModule.IsManagedNode(managedStackNodeIDs[j]))
                    {
                        LogError("Two ModuleB9PartSwitch modules cannot manage the same attach node: " + managedStackNodeIDs[j]);
                        destroy = true;
                    }
                }

                if (otherModule.MaxTempManaged && MaxTempManaged)
                {
                    LogError("Two ModuleB9PartSwitch modules cannot both manage the part's maxTemp");
                    destroy = true;
                }

                if (otherModule.SkinMaxTempManaged && SkinMaxTempManaged)
                {
                    LogError("Two ModuleB9PartSwitch modules cannot both manage the part's skinMaxTemp");
                    destroy = true;
                }

                if (otherModule.AttachNodeManaged && AttachNodeManaged)
                {
                    LogError("Two ModuleB9PartSwitch modules cannot both manage the part's attach node");
                    destroy = true;
                }

                if (destroy)
                {
                    LogWarning("ModuleB9PartSwitch with moduleID '" + otherModule.moduleID + "' is incomatible, and will be removed.");
                    part.Modules.Remove(otherModule);
                    Destroy(otherModule);
                    modifiedSetup = true;
                }
            }

            for (int i = 0; i < part.Modules.Count; i++)
            {
                PartModule m = part.Modules[i];
                if (m == null || m is ModuleB9PartSwitch)
                {
                    continue;
                }
                Type mType = m.GetType();

                for (int j = 0; j < IncompatibleModuleTypes.Length; j++)
                {
                    Type testType = IncompatibleModuleTypes[j];
                    if (mType == testType || mType.IsSubclassOf(testType))
                    {
                        LogError("ModuleB9PartSwitch and " + m.moduleName + " cannot exist on the same part.  " + m.moduleName + " will be removed.");
                        part.Modules.Remove(m);
                        Destroy(m);
                        modifiedSetup = true;
                        break;
                    }
                }
            }

            // If there were incompatible modules, they might have messed with things
            if (modifiedSetup)
            {
                UpdateSubtype(false);
            }
        }
Beispiel #18
0
        public void Setup(ModuleB9PartSwitch parent, bool displayWarnings = true)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent cannot be null");
            }
            if (parent.part == null)
            {
                throw new ArgumentNullException("parent.part cannot be null");
            }

            this.parent = parent;

            aspectLocks.Clear();

            Part part       = parent.part;
            Part partPrefab = part.GetPrefab() ?? part;

            partModifiers.Clear();

            IEnumerable <object> aspectLocksOnOtherModules = parent.PartAspectLocksOnOtherModules;

            string errorString = null;

            void OnInitializationError(string message)
            {
                LogError(message);

                if (displayWarnings)
                {
                    if (errorString == null)
                    {
                        errorString = $"Initialization errors on {parent} subtype '{Name}'";
                    }

                    errorString += "\n  " + message;
                }
            }

            void MaybeAddModifier(IPartModifier modifier)
            {
                if (modifier == null)
                {
                    return;
                }
                if (aspectLocksOnOtherModules.Contains(modifier.PartAspectLock))
                {
                    OnInitializationError($"More than one module can't manage {modifier.Description}");
                }
                else
                {
                    partModifiers.Add(modifier);
                    aspectLocks.Add(modifier.PartAspectLock);
                }
            }

            if (maxTemp > 0)
            {
                MaybeAddModifier(new PartMaxTempModifier(part, partPrefab.maxTemp, maxTemp));
            }

            if (skinMaxTemp > 0)
            {
                MaybeAddModifier(new PartSkinMaxTempModifier(part, partPrefab.skinMaxTemp, skinMaxTemp));
            }

            if (crashTolerance > 0)
            {
                MaybeAddModifier(new PartCrashToleranceModifier(part, partPrefab.crashTolerance, crashTolerance));
            }

            if (attachNode.IsNotNull())
            {
                if (part.attachRules.allowSrfAttach)
                {
                    if (part.srfAttachNode.IsNotNull())
                    {
                        MaybeAddModifier(new PartAttachNodeModifier(part.srfAttachNode, partPrefab.srfAttachNode, attachNode, parent));
                    }
                    else
                    {
                        OnInitializationError("attachNode specified but part does not have a surface attach node");
                    }
                }
                else
                {
                    OnInitializationError("attachNode specified but part does not allow surface attach");
                }
            }

            if (CoMOffset.IsFinite())
            {
                MaybeAddModifier(new PartCoMOffsetModifier(part, partPrefab.CoMOffset, CoMOffset));
            }

            if (CoPOffset.IsFinite())
            {
                MaybeAddModifier(new PartCoPOffsetModifier(part, partPrefab.CoPOffset, CoPOffset));
            }

            if (CoLOffset.IsFinite())
            {
                MaybeAddModifier(new PartCoLOffsetModifier(part, partPrefab.CoLOffset, CoLOffset));
            }

            if (CenterOfBuoyancy.IsFinite())
            {
                MaybeAddModifier(new PartCenterOfBuoyancyModifier(part, partPrefab.CenterOfBuoyancy, CenterOfBuoyancy));
            }

            if (CenterOfDisplacement.IsFinite())
            {
                MaybeAddModifier(new PartCenterOfDisplacementModifier(part, partPrefab.CenterOfDisplacement, CenterOfDisplacement));
            }

            if (stackSymmetry >= 0)
            {
                MaybeAddModifier(new PartStackSymmetryModifier(part, partPrefab.stackSymmetry, stackSymmetry));
            }

            foreach (AttachNodeModifierInfo info in attachNodeModifierInfos)
            {
                MaybeAddModifier(info.CreateAttachNodeModifier(part, parent, OnInitializationError));
            }

            foreach (TextureSwitchInfo info in textureSwitches)
            {
                foreach (TextureReplacement replacement in info.CreateTextureReplacements(part, OnInitializationError))
                {
                    MaybeAddModifier(replacement);
                }
            }

            nodes.Clear();
            foreach (string nodeName in nodeNames)
            {
                string pattern     = '^' + Regex.Escape(nodeName).Replace(@"\*", ".*").Replace(@"\?", ".") + '$';
                Regex  nodeIdRegex = new Regex(pattern);

                bool foundNode = false;

                foreach (AttachNode node in part.attachNodes)
                {
                    if (!nodeIdRegex.IsMatch(node.id))
                    {
                        continue;
                    }

                    foundNode = true;

                    if (node.nodeType != AttachNode.NodeType.Stack)
                    {
                        OnInitializationError($"Node {node.id} is not a stack node, and thus cannot be managed by ModuleB9PartSwitch");
                        continue;
                    }

                    nodes.Add(node);
                    partModifiers.Add(new AttachNodeToggler(node));
                }

                if (!foundNode)
                {
                    OnInitializationError($"No attach nodes matching '{nodeName}' found");
                }
            }

            if (HasTank)
            {
                volumeProvider = new SubtypeVolumeProvider(parent, volumeMultiplier, volumeAdded);
                foreach (TankResource resource in tankType)
                {
                    float            filledProportion = (resource.percentFilled ?? percentFilled ?? tankType.percentFilled ?? 100f) * 0.01f;
                    bool?            tweakable        = resourcesTweakable ?? tankType.resourcesTweakable;
                    ResourceModifier resourceModifier = new ResourceModifier(resource, volumeProvider, part, filledProportion, tweakable);
                    MaybeAddModifier(resourceModifier);
                }
            }

            transforms.Clear();
            foreach (var transformName in transformNames)
            {
                bool foundTransform = false;

                foreach (Transform transform in part.GetModelTransforms(transformName))
                {
                    foundTransform = true;
                    partModifiers.Add(new TransformToggler(transform, part));
                    transforms.Add(transform);
                }

                if (!foundTransform)
                {
                    OnInitializationError($"No transforms named '{transformName}' found");
                }
            }

            foreach (TransformModifierInfo transformModifierInfo in transformModifierInfos)
            {
                foreach (IPartModifier partModifier in transformModifierInfo.CreatePartModifiers(part, OnInitializationError))
                {
                    MaybeAddModifier(partModifier);
                }
            }

            if (!parent.subtypes.Any(subtype => subtype.Name == mirrorSymmetrySubtype))
            {
                OnInitializationError($"Cannot find subtype '{mirrorSymmetrySubtype}' for mirror symmetry subtype");
                mirrorSymmetrySubtype = Name;
            }

            if (errorString.IsNotNull())
            {
                SeriousWarningHandler.DisplaySeriousWarning(errorString);
            }
        }
Beispiel #19
0
        public void Setup(ModuleB9PartSwitch parent)
        {
            if (parent == null)
                throw new ArgumentNullException("parent cannot be null");
            if (parent.part == null)
                throw new ArgumentNullException("parent.part cannot be null");

            this.parent = parent;

            FindObjects();
            FindNodes();
        }
 public static void Spawn(ModuleB9PartSwitch module)
 {
     MaybeCreateResourceRemovalWarning(module, () => CreateDialogue(module));
 }