using System; using System.Collections; using System.Collections.Generic; using UdonSharp; using UnityEngine; using VRC.SDK3.Data; using VRCBoard.Components; using UnityEditor; namespace VRCBoard.Components { [Serializable] internal struct BoardEditorConfigEntry { public const int length = 6; [Header("ID")] public IDType IDType; [Tooltip("Uses with this tag / tier will be displayed")] public string id; [Header("Styling"), Tooltip("Template to use for this tier, %s will be replaced with the supporter name")] public string template; public string separator; [Header("Advanced Options")] public bool userMustBeInWorld; public bool exclusive; public BoardEditorConfigEntry(string[] data) { IDType = (IDType)Enum.Parse(typeof(IDType), data[0]); id = data[1]; template = data[2]; separator = data[3]; userMustBeInWorld = data[4] == "1"; exclusive = data[5] == "1"; } public string[] ToArray() { string[] result = new string[length]; result[0] = IDType.ToString(); result[1] = id; result[2] = template; result[3] = separator; result[4] = userMustBeInWorld ? "1" : "0"; result[5] = exclusive ? "1" : "0"; return result; } } public abstract class VRCBoardSupporterBoardBase : VRCBoardBaseComponent { [SerializeField] internal string Separator = "\\n"; [SerializeField] internal BoardEditorConfigEntry[] ConfigEntries = new BoardEditorConfigEntry[0]; internal int configLength => config.Length/BoardEditorConfigEntry.length; private string GetSeparator(string sep) { sep = sep.Replace("\\n", "\n"); sep = sep.Replace("\\t", "\t"); return sep; } [HideInInspector] public string[] config = new string[0]; private string _supporterList = ""; protected abstract void UpdateSupporterBoard(string supporterList); protected override void OnRegister() { } private string RemovePrefix(string node, string prefix) { if (node.StartsWith(prefix)) return node.Substring(prefix.Length); return node; } private string RemoveSuffix(string str, string suffix) { if (str.EndsWith(suffix)) return str.Substring(0, str.Length - suffix.Length); return str; } private bool GetConfigFromNode(string node, out string[] foundConfig, out int foundIndex) { // for each entry in string[] config get every fifth element // if the first element is equal to the node, return the config //Debug.Log("Getting config from node " + node); foundConfig = null; foundIndex = -1; string[] split = node.Split('.'); string nodeType = split[0].ToLower(); string nodeValue = split[1].ToLower(); //Debug.Log("Length of config: " + config.Length+ " length of entry: " + BoardEditorConfigEntry.length+" config length: " + configLength); for (int i = 0; i < configLength; i++) { string[] thisConfig = new string[BoardEditorConfigEntry.length]; for (int j = 0; j < BoardEditorConfigEntry.length; j++) { thisConfig[j] = config[i * BoardEditorConfigEntry.length + j]; } string id = thisConfig[1]; string idType = thisConfig[0]; //Debug.Log("Checking " + idType + "." + id + " against " + nodeType + "." + nodeValue); if (idType.ToLower() == nodeType && id.ToLower() == nodeValue) { foundConfig = thisConfig; foundIndex = i; return true; } } return false; } private void GetFormattedSupporter(int index) { DataDictionary supporterData = manager.GetSupporterData(index); DataToken supporterName = supporterData["name"]; DataToken perkNodes = supporterData["pn"]; if (supporterName.TokenType != TokenType.String) return; if (perkNodes.TokenType != TokenType.DataList) return; DataList perkNodeList = perkNodes.DataList; int perkNodeCount = perkNodeList.Count; bool reverseSearch = false; for (int i = 0; i < perkNodeCount; i++) { DataToken perkNode = reverseSearch ? perkNodeList[perkNodeCount-1-i] : perkNodeList[i]; if (perkNode.TokenType != TokenType.String) continue; string node = perkNode.String; if (!GetConfigFromNode(node, out string[] foundConfig, out int foundIndex)) continue; string template = foundConfig[2]; string separator = GetSeparator(foundConfig[3]); string formatted = template.Replace("%s", supporterName.String); string result = formatted + separator; string[] newArray = new string[_supporterListSeperated[foundIndex].Length + 1]; for (int j = 0; j < _supporterListSeperated[foundIndex].Length; j++) { newArray[j] = _supporterListSeperated[foundIndex][j]; } newArray[newArray.Length - 1] = result; _supporterListSeperated[foundIndex] = newArray; bool userMustBeInWorld = foundConfig[4] == "1"; bool exclusive = foundConfig[5] == "1"; if (exclusive) break; } } private string[][] _supporterListSeperated; private void ResetSupporterList() { _supporterListSeperated = new string[configLength][]; for (int i = 0; i < configLength; i++) { _supporterListSeperated[i] = new string[0]; } } private void UpdateSupporterList() { ResetSupporterList(); int supporterCount = manager.SupporterCount; _supporterList = ""; for (int i = 0; i < supporterCount; i++) { GetFormattedSupporter(i); } // loop through all the seperated lists and join them with the separator for (int i = 0; i < _supporterListSeperated.Length; i++) { for (int j = 0; j < _supporterListSeperated[i].Length; j++) { _supporterList += _supporterListSeperated[i][j]; } string separator = GetSeparator(config[i * BoardEditorConfigEntry.length + 3]); _supporterList = RemoveSuffix(_supporterList, separator); if (i != _supporterListSeperated.Length - 1) _supporterList += GetSeparator(Separator); } UpdateSupporterBoard(_supporterList); } protected override void OnInitialize() { //Debug.Log("Initializing supporter board component"); } protected override void OnImageDataUpdate() { } protected override void OnImageLoaded() { } protected override void OnSupporterDataUpdate() { UpdateSupporterList(); } } #if UNITY_EDITOR && !UDONSHARP_COMPILER [CustomEditor(typeof(VRCBoardSupporterBoardBase), true)] public class VRCBoardSupporterBoardBaseEditor : Editor { // with methods to serialize and deserialize the config arra public override void OnInspectorGUI() { if (Application.isPlaying) return; //Debug.Log("EDITORRRR"); EditorGUILayout.LabelField("Supporter Board Component"); // apply the editorConfigEntries to config List configList = new List(); VRCBoardSupporterBoardBase targetComponent = (VRCBoardSupporterBoardBase)base.target; BoardEditorConfigEntry[] editorConfigEntries = targetComponent.ConfigEntries; foreach (BoardEditorConfigEntry entry in editorConfigEntries) { configList.AddRange(entry.ToArray()); } targetComponent.config = configList.ToArray(); base.OnInspectorGUI(); } } #endif }