diff --git a/Winch.Examples/ExampleItems/DefaultConfig.json b/Winch.Examples/ExampleItems/DefaultConfig.json new file mode 100644 index 00000000..83f50daa --- /dev/null +++ b/Winch.Examples/ExampleItems/DefaultConfig.json @@ -0,0 +1,45 @@ +{ + "Toggle": { + "type": "toggle", + "title": "exampleconfig.toggle.title", + "tooltip": "exampleconfig.toggle.tooltip", + "value": true + }, + "Text": { + "type": "text", + "title": "exampleconfig.text.title", + "tooltip": "exampleconfig.text.tooltip", + "value": "Hello" + }, + "Integer": { + "type": "integer", + "title": "exampleconfig.integer.title", + "tooltip": "exampleconfig.integer.tooltip", + "value": 22 + }, + "Decimal": { + "type": "decimal", + "title": "exampleconfig.decimal.title", + "tooltip": "exampleconfig.decimal.tooltip", + "value": 1.01 + }, + "Slider": { + "type": "slider", + "title": "exampleconfig.slider.title", + "tooltip": "exampleconfig.slider.tooltip", + "min": 10, + "max": 20, + "value": 15 + }, + "Dropdown": { + "type": "dropdown", + "title": "exampleconfig.dropdown.title", + "tooltip": "exampleconfig.dropdown.tooltip", + "options": [ + "exampleconfig.dropdown.option.one", + "exampleconfig.dropdown.option.two", + "exampleconfig.dropdown.option.three" + ], + "value": "exampleconfig.dropdown.option.two" + } +} diff --git a/Winch.Examples/ExampleItems/ExampleItems.csproj b/Winch.Examples/ExampleItems/ExampleItems.csproj index 9b745d83..f6b841bb 100644 --- a/Winch.Examples/ExampleItems/ExampleItems.csproj +++ b/Winch.Examples/ExampleItems/ExampleItems.csproj @@ -44,6 +44,9 @@ Always + + Always + Always diff --git a/Winch/Assets/Localization/en.json b/Winch/Assets/Localization/en.json new file mode 100644 index 00000000..afe5e9a4 --- /dev/null +++ b/Winch/Assets/Localization/en.json @@ -0,0 +1,24 @@ +{ + "menu.mods": "Mods", + "popup.confirm-restore-mod-default-settings": "Are you sure you want to restore this mod's settings back to default values?\n\nThis action cannot be undone.", + "settings.tab.mods": "Mods", + "settings.mods.footer.list": "List", + "settings.mods.footer.options": "Options", + "winch.name": "Winch", + "winch.writelogstofile.title": "Write logs to file", + "winch.writelogstofile.tooltip": "Whether to write logs to a file.\n\nREQUIRES RESTART TO TAKE EFFECT.", + "winch.writelogstoconsole.title": "Write logs to console", + "winch.writelogstoconsole.tooltip": "Whether to open the console program.\n\nREQUIRES RESTART TO TAKE EFFECT.", + "winch.loglevel.title": "Minimum Log Level", + "winch.loglevel.tooltip": "The minimum log level that can pushed to file and console.", + "winch.logsfolder.title": "Logs Folder", + "winch.logsfolder.tooltip": "The path to the logs folder. Can be relative to the game folder or absolute.\n\nREQUIRES RESTART TO TAKE EFFECT.", + "winch.detailedlogsources.title": "Detailed Log Sources", + "winch.detailedlogsources.tooltip": "Whether to show the class and method the log came from instead of just the assembly name.", + "winch.enabledeveloperconsole.title": "Enable Developer Console", + "winch.enabledeveloperconsole.tooltip": "Whether to enable the terminal that can opened with the ` key.", + "winch.maxlogfiles.title": "Max Log Files", + "winch.maxlogfiles.tooltip": "The maximum amount of logs that can be in the log folder.\n\nREQUIRES RESTART TO TAKE EFFECT.", + "winch.exportyarnprogram.title": "Export Yarn Program", + "winch.exportyarnprogram.tooltip": "Whether to extract the game's Yarn dialogue program to 'YarnProgramVanilla.txt' and 'YarnProgramModded.txt' in the game folder.\n\nREQUIRES RESTART TO TAKE EFFECT." +} \ No newline at end of file diff --git a/Winch/Components/ColorDropdownInput.cs b/Winch/Components/ColorDropdownInput.cs new file mode 100644 index 00000000..e9c6b948 --- /dev/null +++ b/Winch/Components/ColorDropdownInput.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using TMPro; +using UnityEngine; +using UnityEngine.Localization; +using UnityEngine.Localization.Settings; +using UnityEngine.UI; +using Winch.Util; + +namespace Winch.Components +{ + public class ColorDropdownInput : DropdownInput + { + [SerializeField] + internal TextMeshProUGUI textField; + + [SerializeField] + private LocalizedString labelLocalizedString = LocalizationUtil.CreateReference("Strings", "settings.dropdown.color"); + + [SerializeField] + internal int columns; + + protected override void Awake() + { + populateOptions = true; + retrieveSelectedIndex = true; + scrollToSelectedItem = false; + optionStrings = GameManager.Instance.GameConfigData.Colors.Select(color => labelLocalizedString).ToList(); + base.Awake(); + initialized = true; + } + + protected override void OnLocaleChanged(Locale l) + { + RefreshDropdown(); + } + + protected override void RefreshDropdown() + { + base.RefreshDropdown(); + RefreshLabelsColor(); + } + + private void RefreshLabelsColor() + { + textField.color = GameManager.Instance.GameConfigData.Colors[CurrentIndex]; + } + + protected override void RetrieveSelectedIndex() + { + base.RetrieveSelectedIndex(); + } + + protected override void OnValueChanged(int value) + { + base.OnValueChanged(value); + RefreshLabelsColor(); + } + + protected override void OnComponentSubmitted() + { + List items = transform.GetComponentsInChildren().ToList(); + for (int i = 0; i < items.Count; i++) + { + var item = items[i]; + item.background.color = GameManager.Instance.GameConfigData.Colors[i]; + Selectable toggle = item.toggle; + Navigation navigation = toggle.navigation; + var floatedColumns = (float)columns; + if (i <= Mathf.Ceil((items.Count / floatedColumns) * floatedColumns) && i + columns < items.Count) + { + Selectable toggle2 = items[i + columns].toggle; + navigation.selectOnDown = toggle2; + } + if (i >= columns && i - columns >= 0) + { + Selectable toggle3 = items[i - columns].toggle; + navigation.selectOnUp = toggle3; + } + toggle.navigation = navigation; + } + } + + protected override void ChangeValue(int index) + { + SetConfigValue(GameManager.Instance.GameConfigData.Colors[index]); + } + } +} \ No newline at end of file diff --git a/Winch/Components/DecimalFieldInput.cs b/Winch/Components/DecimalFieldInput.cs new file mode 100644 index 00000000..ca0d35ea --- /dev/null +++ b/Winch/Components/DecimalFieldInput.cs @@ -0,0 +1,32 @@ + +namespace Winch.Components +{ + public class DecimalFieldInput : FieldInput + { + protected override void Awake() + { + base.Awake(); + //inputField.characterValidation = TMPro.TMP_InputField.CharacterValidation.Decimal; + } + + protected override bool ValidateChar(char addedChar) + { + if (IsNRT(addedChar)) + { + return false; + } + return char.IsDigit(addedChar) || addedChar == '.'; + } + + protected override void ChangeValue(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + SetConfigValue(GetDefaultConfigValue()); + return; + } + + SetConfigValue(double.Parse(value)); + } + } +} \ No newline at end of file diff --git a/Winch/Components/DropdownInput.cs b/Winch/Components/DropdownInput.cs new file mode 100644 index 00000000..08c5970e --- /dev/null +++ b/Winch/Components/DropdownInput.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using TMPro; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.Localization; +using UnityEngine.Localization.Components; +using UnityEngine.Localization.Settings; +using Winch.Core; +using Winch.Data.Item; +using Winch.Util; + +namespace Winch.Components +{ + public class DropdownInput : Input + { + [SerializeField] + public bool populateOptions = true; + + [SerializeField] + public bool retrieveSelectedIndex = true; + + [SerializeField] + public bool scrollToSelectedItem = true; + + [SerializeField] + protected internal TextMeshProUGUI selectedValueTextField; + + [SerializeField] + protected internal TMP_Dropdown dropdown; + + [SerializeField] + protected internal SettingsUIComponentEventNotifier dropdownEventNotifier; + + [SerializeField] + protected List optionStrings = new List() { LocalizationUtil.Unknown }; + + [SerializeField] + protected List options = new List() { "Unknown" }; + + protected bool initialized = false; + + public int CurrentIndex + { + get => dropdown.value; + set => dropdown.value = value; + } + + public LocalizedString CurrentLocalizedOption + { + get => optionStrings[CurrentIndex]; + } + + public string CurrentOption + { + get => options[CurrentIndex]; + } + + protected virtual void Awake() + { + dropdown.onValueChanged.AddListener(OnValueChanged); + } + + protected virtual void OnComponentSubmitted() + { + if (scrollToSelectedItem) + { + var selectedItem = dropdown.GetComponentsInChildren().ElementAtOrDefault(CurrentIndex); + if (selectedItem != null) + { + selectedItem.ScrollToThis(); + } + } + } + + protected virtual void OnEnable() + { + dropdownEventNotifier.OnComponentSubmitted += OnComponentSubmitted; + ApplicationEvents.Instance.OnLocaleChanged += OnLocaleChanged; + RefreshDropdown(); + } + + protected virtual void OnDisable() + { + dropdownEventNotifier.OnComponentSubmitted -= OnComponentSubmitted; + ApplicationEvents.Instance.OnLocaleChanged -= OnLocaleChanged; + } + + protected virtual void OnLocaleChanged(Locale l) + { + RefreshDropdownStrings(); + } + + protected void AddOptionString(LocalizedString str) + { + TMP_Dropdown.OptionData optionData = new TMP_Dropdown.OptionData(); + optionData.text = LocalizationSettings.StringDatabase.GetLocalizedString(str.TableEntryReference, null, FallbackBehavior.UseProjectSettings, Array.Empty()); + dropdown.options.Add(optionData); + } + + protected virtual void RefreshDropdownStrings() + { + if (populateOptions) + { + dropdown.options.Clear(); + optionStrings.ForEach(AddOptionString); + selectedValueTextField.text = LocalizationSettings.StringDatabase.GetLocalizedString(optionStrings[CurrentIndex].TableEntryReference, null, FallbackBehavior.UseProjectSettings, Array.Empty()); + } + } + + protected virtual void RefreshDropdown() + { + RefreshDropdownStrings(); + RetrieveSelectedIndex(); + } + + protected virtual void RetrieveSelectedIndex() + { + if (!initialized) return; + if (!retrieveSelectedIndex) return; + + SetSelectedOption(GetConfigValue() ?? string.Empty); + } + + protected internal virtual void SetSelectedOption(string option) + { + SetSelectedIndex(!string.IsNullOrWhiteSpace(option) ? options.IndexOf(option) : -1); + } + + protected internal virtual void SetSelectedIndex(int index) + { + if (index <= -1) index = 0; + + dropdown.SetValueWithoutNotify(index); + } + + protected virtual void OnValueChanged(int index) + { + if (!initialized) return; + WinchCore.Log.Debug(string.Format("[DropdownInput:{0}] OnValueChanged({1})", base.name, index)); + ChangeValue(index); + } + + protected virtual void ChangeValue(int index) + { + if (!initialized) return; + SetConfigValue(options[index]); + } + + public override void OnForceRefresh() + { + RefreshDropdown(); + } + + protected internal virtual void Initialize(string value, string[] options) + { + this.options = options.ToList(); + var newOptionStrings = new List(); + for (int i = 0; i < options.Length; i++) + { + var option = options[i]; + if (value == option) + { + SetSelectedIndex(i); + } + newOptionStrings.Add(LocalizationUtil.CreateReference("Strings", option)); + } + optionStrings = newOptionStrings; + RefreshDropdown(); + SetSelectedOption(value); + initialized = true; + } + } +} diff --git a/Winch/Components/FieldInput.cs b/Winch/Components/FieldInput.cs new file mode 100644 index 00000000..e3278d17 --- /dev/null +++ b/Winch/Components/FieldInput.cs @@ -0,0 +1,146 @@ +using Google.Protobuf.WellKnownTypes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using TMPro; +using UnityEngine; +using UnityEngine.Localization; +using UnityEngine.Localization.Components; +using UnityEngine.UI; +using Winch.Core; +using Winch.Util; +using static UnityEngine.Rendering.DebugUI; + +namespace Winch.Components +{ + public class FieldInput : Input + { + [SerializeField] + protected internal TMP_InputField inputField; + [SerializeField] + protected internal Label placeholder; + + private bool initialized = false; + + public string InputFieldText + { + get => inputField.text; + set + { + inputField.text = value; + OnValueChanged(value); + OnEndEdit(value); + } + } + + public void SetInputFieldTextWithNoNotify(string value) + { + inputField.text = value; + } + + public void ClearInputFieldText() + { + InputFieldText = string.Empty; + } + + public void ClearInputFieldTextWithNoNotify() + { + SetInputFieldTextWithNoNotify(string.Empty); + } + + protected virtual void Awake() + { + inputField.ActivateInputField(); + inputField.onSelect.AddListener(OnSelect); + inputField.onDeselect.AddListener(OnDeselect); + inputField.onEndEdit.AddListener(OnEndEdit); + inputField.onValueChanged.AddListener(OnValueChanged); + inputField.onValidateInput += OnValidateInput; + InitializePlaceholder(); + } + + protected virtual void InitializePlaceholder() + { + placeholder.LabelString = GetDefaultConfigValue(); + } + + private void OnSelect(string value) + { + if (!initialized) return; + WinchCore.Log.Debug(string.Format("[FieldInput:{0}] OnSelect({1})", base.name, value)); + } + + private void OnDeselect(string value) + { + if (!initialized) return; + WinchCore.Log.Debug(string.Format("[FieldInput:{0}] OnDeselect({1})", base.name, value)); + ChangeValue(value); + } + + private void OnEndEdit(string value) + { + if (!initialized) return; + WinchCore.Log.Debug(string.Format("[FieldInput:{0}] OnEndEdit({1})", base.name, value)); + ChangeValue(value); + } + + protected virtual void ChangeValue(string value) + { + if (!initialized) return; + if (string.IsNullOrWhiteSpace(value)) + { + var defaultValue = GetDefaultConfigValue() ?? string.Empty; + SetConfigValue(defaultValue); + return; + } + + SetConfigValue(value); + } + + protected virtual void OnValueChanged(string value) + { + if (!initialized) return; + WinchCore.Log.Debug(string.Format("[FieldInput:{0}] OnValueChanged({1})", base.name, value)); + } + + private char OnValidateInput(string input, int charIndex, char addedChar) + { + WinchCore.Log.Debug(string.Format("[FieldInput:{0}] OnValidateInput({1})", base.name, addedChar)); + return ValidateChar(addedChar) ? addedChar : '\0'; + } + + protected virtual bool ValidateChar(char addedChar) + { + return !IsNRT(addedChar); + } + + protected bool IsNRT(char addedChar) + { + if (addedChar == '\n' || addedChar == '\r') + { + inputField.DeactivateInputField(); + return true; + } + if (addedChar == '\t') + { + return true; + } + return false; + } + + public override void OnForceRefresh() + { + InitializePlaceholder(); + SetInputFieldTextWithNoNotify(GetConfigValue()); + } + + protected internal virtual void Initialize(string value) + { + InitializePlaceholder(); + SetInputFieldTextWithNoNotify(value); + initialized = true; + } + } +} diff --git a/Winch/Components/Input.cs b/Winch/Components/Input.cs new file mode 100644 index 00000000..5d953d75 --- /dev/null +++ b/Winch/Components/Input.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Localization; +using UnityEngine.Localization.Components; +using UnityEngine.Rendering.Universal; +using Winch.Config; +using Winch.Core; +using Winch.Util; + +namespace Winch.Components +{ + public abstract class Input : MonoBehaviour, ISettingsRefreshable + { + [SerializeField] + internal bool isWinch => modName == WinchCore.GUID; + + [SerializeField] + protected internal string modName = string.Empty; + + [SerializeField] + protected internal string key = string.Empty; + + [SerializeField] + protected internal LocalizeStringEvent localizedStringField; + + [SerializeField] + protected internal TextTooltipRequester textTooltipRequester; + + [SerializeField] + protected LocalizedString localizedString = LocalizationUtil.Unknown; + + [SerializeField] + protected LocalizedString tooltipDescriptionString = LocalizationUtil.Empty; + + public LocalizedString TitleString + { + get => localizedString; + set + { + localizedString = value; + if (localizedStringField != null) + { + localizedStringField.StringReference = value; + } + if (textTooltipRequester != null) + { + textTooltipRequester.LocalizedTitleKey = value; + } + } + } + + public LocalizedString TooltipDescriptionString + { + get => tooltipDescriptionString; + set + { + tooltipDescriptionString = value; + if (textTooltipRequester != null) + { + if (value.IsEmpty) + { + textTooltipRequester.enabled = false; + } + else + { + textTooltipRequester.LocalizedDescriptionKey = value; + textTooltipRequester.enabled = true; + } + } + } + } + + protected virtual void Start() + { + localizedStringField.OnUpdateString.Invoke(string.Empty); + localizedStringField.StringReference = localizedString; + textTooltipRequester.LocalizedTitleKey = localizedString; + if (tooltipDescriptionString.IsEmpty) + { + textTooltipRequester.enabled = false; + } + else + { + textTooltipRequester.LocalizedDescriptionKey = tooltipDescriptionString; + textTooltipRequester.enabled = true; + } + } + + public void ForceRefresh() + { + try + { + OnForceRefresh(); + gameObject.Activate(); + } + catch + { + gameObject.Deactivate(); + } + } + + public abstract void OnForceRefresh(); + + protected internal virtual T? GetConfigValue() + { + if (isWinch) + { + return WinchConfig.GetProperty(key); + } + + if (ModConfig.TryGetConfig(modName, out var config)) + { + return config.GetProperty(key, default(T)); + } + + return default(T); + } + + protected internal virtual T? GetDefaultConfigValue() + { + try + { + if (isWinch) + { + return WinchConfig.GetDefaultProperty(key); + } + + if (ModConfig.TryGetConfig(modName, out var config)) + { + return config.GetDefaultProperty(key); + } + } + catch (Exception ex) + { + WinchCore.Log.Error(ex.Message); + } + return default(T); + } + + protected internal virtual void SetConfigValue(T value) + { + if (isWinch) + { + WinchConfig.SetProperty(key, value); + return; + } + + if (ModConfig.TryGetConfig(modName, out var config)) + { + config.SetProperty(key, value); + } + } + } +} diff --git a/Winch/Components/IntegerFieldInput.cs b/Winch/Components/IntegerFieldInput.cs new file mode 100644 index 00000000..9504592f --- /dev/null +++ b/Winch/Components/IntegerFieldInput.cs @@ -0,0 +1,32 @@ + +namespace Winch.Components +{ + public class IntegerFieldInput : FieldInput + { + protected override void Awake() + { + base.Awake(); + //inputField.characterValidation = TMPro.TMP_InputField.CharacterValidation.Integer; + } + + protected override bool ValidateChar(char addedChar) + { + if (IsNRT(addedChar)) + { + return false; + } + return char.IsNumber(addedChar); + } + + protected override void ChangeValue(string value) + { + if (string.IsNullOrWhiteSpace(value)) + { + SetConfigValue(GetDefaultConfigValue()); + return; + } + + SetConfigValue(long.Parse(value)); + } + } +} \ No newline at end of file diff --git a/Winch/Components/Label.cs b/Winch/Components/Label.cs new file mode 100644 index 00000000..e712e433 --- /dev/null +++ b/Winch/Components/Label.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Localization.Components; +using UnityEngine.Localization; +using Winch.Util; +using TMPro; + +namespace Winch.Components +{ + public class Label : MonoBehaviour + { + private string labelString = "Unknown"; + public string LabelString + { + get => labelString; + set + { + labelString = value; + UpdateLabel(); + } + } + public TextMeshProUGUI textField => GetComponentInChildren(true); + + public void OnEnable() + { + textField.text = string.Empty; + UpdateLabel(); + } + + public void UpdateLabel() + { + textField.text = LabelString; + } + } +} diff --git a/Winch/Components/LocalizedLabel.cs b/Winch/Components/LocalizedLabel.cs new file mode 100644 index 00000000..202edb19 --- /dev/null +++ b/Winch/Components/LocalizedLabel.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Localization.Components; +using UnityEngine.Localization; +using Winch.Util; +using TMPro; + +namespace Winch.Components +{ + public class LocalizedLabel : MonoBehaviour + { + private LocalizedString labelString = LocalizationUtil.Unknown; + public LocalizedString LabelString + { + get => labelString; + set + { + labelString = value; + UpdateLabel(); + } + } + public LocalizeStringEvent LocalizedStringEvent => GetComponentInChildren(true); + + public void OnEnable() + { + LocalizedStringEvent.OnUpdateString.Invoke(string.Empty); + UpdateLabel(); + } + + public void UpdateLabel() + { + LocalizedStringEvent.StringReference = LabelString; + } + } +} diff --git a/Winch/Components/ModsButton.cs b/Winch/Components/ModsButton.cs new file mode 100644 index 00000000..1b1fe590 --- /dev/null +++ b/Winch/Components/ModsButton.cs @@ -0,0 +1,24 @@ +using UnityEngine; +using UnityEngine.Localization; +using UnityEngine.Localization.Components; +using Winch.Core; +using Winch.Patches; + +namespace Winch.Components +{ + public class ModsButton : MonoBehaviour + { + public static int modsTabIndex = -1; + + public void Awake() + { + GetComponent().OnClick += OnClick; + } + + public void OnClick() + { + ApplicationEvents.Instance.TriggerToggleSettings(true); + GameObject.FindObjectOfType().dialog.ShowNewPanel(modsTabIndex, true); + } + } +} diff --git a/Winch/Components/ModsTab.cs b/Winch/Components/ModsTab.cs new file mode 100644 index 00000000..489ee26a --- /dev/null +++ b/Winch/Components/ModsTab.cs @@ -0,0 +1,578 @@ +using UnityEngine; +using UnityEngine.Localization.Components; +using UnityEngine.Localization; +using Coffee.UIExtensions; +using UnityEngine.UIElements; +using Winch.Util; +using Winch.Core; +using UnityEngine.UI; +using System.Collections.Generic; +using System.Linq; +using Winch.Config; +using Galaxy.Api; +using TMPro; +using Sirenix.Utilities; +using Newtonsoft.Json.Linq; +using System; +using InControl; +using static Mono.Security.X509.X520; +using AeLa.EasyFeedback; + +namespace Winch.Components +{ + internal class ModsTab : MonoBehaviour + { + internal static LocalizedString winchHeader = LocalizationUtil.CreateReference("Strings", "winch.name"); + internal static LocalizedString tabHeader = LocalizationUtil.CreateReference("Strings", "settings.tab.mods"); + internal static LocalizedString footerList = LocalizationUtil.CreateReference("Strings", "settings.mods.footer.list"); + internal static LocalizedString footerOptions = LocalizationUtil.CreateReference("Strings", "settings.mods.footer.options"); + + private static ModsTab instance; + internal static ModsTab Instance => instance; + + public static bool isActive => Instance.settingsDialog.dialog.CurrentIndex == ModsButton.modsTabIndex; + + internal Label labelPrefab; + internal BasicButtonWrapper buttonPrefab; + internal DropdownInput dropdownPrefab; + internal ColorDropdownInput colorDropdownPrefab; + internal OnOffDropdownInput onOffDropdownPrefab; + internal SliderInput sliderPrefab; + internal FieldInput inputFieldPrefab; + internal IntegerFieldInput integerInputFieldPrefab; + internal DecimalFieldInput decimalInputFieldPrefab; + internal SettingsDialog settingsDialog; + internal TabbedPanel panel; + internal TabUI tab; + internal Transform header; + internal Label headerText; + internal LocalizedLabel headerTextLocalized; + internal Transform footer; + internal LocalizedLabel footerText; + internal BasicButtonWrapper footerButton; + internal Transform list; + internal Transform options; + internal ScrollRect listScroller; + internal ScrollRect optionsScroller; + internal bool currentWinch; + internal ModAssembly currentMod; + private List modButtons = new List(); + private List