public static void addChildren(CustomOption option, ref StringBuilder entry, bool indent = true)
        {
            if (!option.enabled)
            {
                return;
            }

            foreach (var child in option.children)
            {
                if (!child.isHidden)
                {
                    entry.AppendLine((indent ? "    " : "") + GameOptionsDataPatch.optionToString(child));
                }
                addChildren(child, ref entry, indent);
            }
        }
        public static bool initializeOverlays()
        {
            HudManager hudManager = DestroyableSingleton <HudManager> .Instance;

            if (hudManager == null)
            {
                return(false);
            }

            if (helpButton == null)
            {
                helpButton = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.HelpButton.png", 115f);
            }

            if (colorBG == null)
            {
                colorBG = Helpers.loadSpriteFromResources("TheOtherRoles.Resources.White.png", 100f);
            }

            if (meetingUnderlay == null)
            {
                meetingUnderlay = UnityEngine.Object.Instantiate(hudManager.FullScreen, hudManager.transform);
                meetingUnderlay.transform.localPosition = new Vector3(0f, 0f, 20f);
                meetingUnderlay.gameObject.SetActive(true);
                meetingUnderlay.enabled = false;
            }

            if (infoUnderlay == null)
            {
                infoUnderlay = UnityEngine.Object.Instantiate(meetingUnderlay, hudManager.transform);
                infoUnderlay.transform.localPosition = new Vector3(0f, 0f, -900f);
                infoUnderlay.gameObject.SetActive(true);
                infoUnderlay.enabled = false;
            }

            if (infoOverlayRules == null)
            {
                infoOverlayRules          = UnityEngine.Object.Instantiate(hudManager.TaskText, hudManager.transform);
                infoOverlayRules.fontSize = infoOverlayRules.fontSizeMin = infoOverlayRules.fontSizeMax = 1.15f;
                infoOverlayRules.autoSizeTextContainer   = false;
                infoOverlayRules.enableWordWrapping      = false;
                infoOverlayRules.alignment               = TMPro.TextAlignmentOptions.TopLeft;
                infoOverlayRules.transform.position      = Vector3.zero;
                infoOverlayRules.transform.localPosition = new Vector3(-2.5f, 1.15f, -910f);
                infoOverlayRules.transform.localScale    = Vector3.one;
                infoOverlayRules.color   = Palette.White;
                infoOverlayRules.enabled = false;
            }

            if (infoOverlayRoles == null)
            {
                infoOverlayRoles = UnityEngine.Object.Instantiate(infoOverlayRules, hudManager.transform);
                infoOverlayRoles.maxVisibleLines         = 28;
                infoOverlayRoles.fontSize                = infoOverlayRoles.fontSizeMin = infoOverlayRoles.fontSizeMax = 1.15f;
                infoOverlayRoles.outlineWidth           += 0.02f;
                infoOverlayRoles.autoSizeTextContainer   = false;
                infoOverlayRoles.enableWordWrapping      = false;
                infoOverlayRoles.alignment               = TMPro.TextAlignmentOptions.TopLeft;
                infoOverlayRoles.transform.position      = Vector3.zero;
                infoOverlayRoles.transform.localPosition = infoOverlayRules.transform.localPosition + new Vector3(2.5f, 0.0f, 0.0f);
                infoOverlayRoles.transform.localScale    = Vector3.one;
                infoOverlayRoles.color   = Palette.White;
                infoOverlayRoles.enabled = false;
            }

            if (roleUnderlay == null)
            {
                roleUnderlay = UnityEngine.Object.Instantiate(meetingUnderlay, hudManager.transform);
                roleUnderlay.transform.localPosition = new Vector3(0f, 0f, -900f);
                roleUnderlay.gameObject.SetActive(true);
                roleUnderlay.enabled = false;
            }

            if (roleOverlayList == null)
            {
                roleOverlayList = new TMPro.TextMeshPro[3];
            }

            for (var i = 0; i < roleOverlayList.Length; i++)
            {
                if (roleOverlayList[i] == null)
                {
                    if (i == 0)
                    {
                        roleOverlayList[i] = UnityEngine.Object.Instantiate(hudManager.TaskText, hudManager.transform);

                        initializeRoleOverlay(roleOverlayList[i]);

                        roleOverlayList[i].transform.localPosition = new Vector3(-3.5f, 1.2f, -910f);
                    }
                    else
                    {
                        roleOverlayList[i] = UnityEngine.Object.Instantiate(roleOverlayList[i - 1], hudManager.transform);

                        initializeRoleOverlay(roleOverlayList[i]);

                        roleOverlayList[i].transform.localPosition = roleOverlayList[i - 1].transform.localPosition + new Vector3(3.1f, 0.0f, 0.0f);
                    }
                }
            }

            if (roleDatas == null)
            {
                roleDatas = new List <string>();

                StringBuilder entry   = new StringBuilder();
                List <string> entries = new List <string>();

                // First add the presets and the role counts
                entries.Add(GameOptionsDataPatch.optionToString(CustomOptionHolder.presetSelection));

                var optionName = CustomOptionHolder.cs(new Color(204f / 255f, 204f / 255f, 0, 1f), ModTranslation.getString("crewmateRoles"));
                var min        = CustomOptionHolder.crewmateRolesCountMin.getSelection();
                var max        = CustomOptionHolder.crewmateRolesCountMax.getSelection();
                if (min > max)
                {
                    min = max;
                }
                var optionValue = (min == max) ? $"{max}" : $"{min} - {max}";
                entry.AppendLine($"{optionName}: {optionValue}");

                optionName = CustomOptionHolder.cs(new Color(204f / 255f, 204f / 255f, 0, 1f), ModTranslation.getString("neutralRoles"));
                min        = CustomOptionHolder.neutralRolesCountMin.getSelection();
                max        = CustomOptionHolder.neutralRolesCountMax.getSelection();
                if (min > max)
                {
                    min = max;
                }
                optionValue = (min == max) ? $"{max}" : $"{min} - {max}";
                entry.AppendLine($"{optionName}: {optionValue}");

                optionName = CustomOptionHolder.cs(new Color(204f / 255f, 204f / 255f, 0, 1f), ModTranslation.getString("impostorRoles"));
                min        = CustomOptionHolder.impostorRolesCountMin.getSelection();
                max        = CustomOptionHolder.impostorRolesCountMax.getSelection();
                if (min > max)
                {
                    min = max;
                }
                optionValue = (min == max) ? $"{max}" : $"{min} - {max}";
                entry.AppendLine($"{optionName}: {optionValue}");

                entries.Add(entry.ToString().Trim('\r', '\n'));

                int maxLines = 28;

                foreach (CustomOption option in CustomOption.options)
                {
                    if ((option == CustomOptionHolder.presetSelection) ||
                        (option == CustomOptionHolder.crewmateRolesCountMin) ||
                        (option == CustomOptionHolder.crewmateRolesCountMax) ||
                        (option == CustomOptionHolder.neutralRolesCountMin) ||
                        (option == CustomOptionHolder.neutralRolesCountMax) ||
                        (option == CustomOptionHolder.impostorRolesCountMin) ||
                        (option == CustomOptionHolder.impostorRolesCountMax))
                    {
                        continue;
                    }

                    if (option.parent == null)
                    {
                        if (!option.enabled)
                        {
                            continue;
                        }

                        entry = new StringBuilder();
                        if (!option.isHidden)
                        {
                            entry.AppendLine(GameOptionsDataPatch.optionToString(option));
                        }

                        addChildren(option, ref entry, !option.isHidden);

                        // 1つのオプションが最大行を越えていた場合、最大行までで分割する
                        int lines = entry.ToString().Trim('\r', '\n').Count(c => c == '\n') + 1;
                        while (lines > maxLines)
                        {
                            var line       = 0;
                            var newEntry   = new StringBuilder();
                            var entryLines = entry.ToString().Trim('\r', '\n').Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None);
                            foreach (var entryLine in entryLines)
                            {
                                newEntry.AppendLine(entryLine);
                                entry.Remove(0, entryLine.Length + Environment.NewLine.Length);
                                line++;
                                if (maxLines <= line)
                                {
                                    break;
                                }
                            }
                            entries.Add(newEntry.ToString().Trim('\r', '\n'));
                            lines -= maxLines;
                        }

                        entries.Add(entry.ToString().Trim('\r', '\n'));
                    }
                }

                int    lineCount = 0;
                string page      = "";
                foreach (var e in entries)
                {
                    int lines = e.Count(c => c == '\n') + 1;

                    if (lineCount + lines > maxLines)
                    {
                        roleDatas.Add(page);
                        page      = "";
                        lineCount = 0;
                    }

                    page       = page + e + "\n\n";
                    lineCount += lines + 1;
                }

                page = page.Trim('\r', '\n');
                if (page != "")
                {
                    roleDatas.Add(page);
                }

                maxRolePage = ((roleDatas.Count - 1) / 3) + 1;
            }

            return(true);
        }
        public static void showInfoOverlay()
        {
            if (overlayShown || MapOptions.hideSettings)
            {
                return;
            }

            HudManager hudManager = DestroyableSingleton <HudManager> .Instance;

            if (ShipStatus.Instance == null || PlayerControl.LocalPlayer == null || hudManager == null || HudManager.Instance.IsIntroDisplayed || (!PlayerControl.LocalPlayer.CanMove && MeetingHud.Instance == null))
            {
                return;
            }

            if (!initializeOverlays())
            {
                return;
            }

            hideRoleOverlay();

            if (MapBehaviour.Instance != null)
            {
                MapBehaviour.Instance.Close();
            }

            hudManager.SetHudActive(false);

            overlayShown = true;

            Transform parent;

            if (MeetingHud.Instance != null)
            {
                parent = MeetingHud.Instance.transform;
            }
            else
            {
                parent = hudManager.transform;
            }

            infoUnderlay.transform.parent     = parent;
            infoOverlayRules.transform.parent = parent;
            infoOverlayRoles.transform.parent = parent;

            infoUnderlay.sprite = colorBG;
            infoUnderlay.color  = new Color(0.1f, 0.1f, 0.1f, 0.88f);
            infoUnderlay.transform.localScale = new Vector3(7.5f, 5f, 1f);
            infoUnderlay.enabled = true;

            TheOtherRolesPlugin.optionsPage = 0;
            GameOptionsData o           = PlayerControl.GameOptions;
            List <string>   gameOptions = o.ToString().Split("\n", StringSplitOptions.RemoveEmptyEntries).ToList().GetRange(2, 17);

            infoOverlayRules.text    = string.Join("\n", gameOptions) + "\n\n" + GameOptionsDataPatch.optionsToString(CustomOptionHolder.specialOptions);
            infoOverlayRules.enabled = true;

            string rolesText = "";

            foreach (RoleInfo r in RoleInfo.getRoleInfoForPlayer(PlayerControl.LocalPlayer))
            {
                string roleOptions = r.roleOptions;
                string roleDesc    = r.fullDescription;
                rolesText += $"<size=150%>{r.nameColored}</size>" +
                             (roleDesc != "" ? $"\n{r.fullDescription}" : "") + "\n\n" +
                             (roleOptions != "" ? $"{roleOptions}\n\n" : "");
            }

            infoOverlayRoles.text    = rolesText;
            infoOverlayRoles.enabled = true;

            var underlayTransparent = new Color(0.1f, 0.1f, 0.1f, 0.0f);
            var underlayOpaque      = new Color(0.1f, 0.1f, 0.1f, 0.88f);

            HudManager.Instance.StartCoroutine(Effects.Lerp(0.2f, new Action <float>(t =>
            {
                infoUnderlay.color     = Color.Lerp(underlayTransparent, underlayOpaque, t);
                infoOverlayRules.color = Color.Lerp(Palette.ClearWhite, Palette.White, t);
                infoOverlayRoles.color = Color.Lerp(Palette.ClearWhite, Palette.White, t);
            })));
        }