using System; using System.Linq; using System.Collections.Generic; using UnityEngine; using System.Collections; using TMPro; using UnityEditor; /// <summary> /// <see cref="Fact.Id"/>/ <c>MonoBehaviour</c> wrapper to be attached to <see cref="Fact.Representation"/> /// </summary> [DisallowMultipleComponent] public class FactObject : FactWrapper, ISerializationCallbackReceiver { public enum FactMaterials { Default = 0, Selected = 1, Hint = 2, Solution = 3, } [SerializeField] protected List<TMP_Text> FactText; [SerializeField] protected List<string> StringLabelFormats; [NonSerialized] public Material[] materials; public new Renderer[] renderer; [NonSerialized] protected List<FactObject> AllChildren; #region Unity Serialization public Material Default; public Material Selected; public Material Hint; public Material Solution; void ISerializationCallbackReceiver.OnBeforeSerialize() { if (FactText == null || FactText.Count() == 0) { FactText = transform .GetComponentsInChildren<TMP_Text>(includeInactive: true) .ToList(); } { StringLabelFormats ??= new(); int i = StringLabelFormats.Count(); int end = FactText.Count(); for (; i < end; i++) StringLabelFormats.Add("{" + (i + 1) + "}"); } if (materials != null) { Default = materials[(int)FactMaterials.Default]; Selected = materials[(int)FactMaterials.Selected]; Hint = materials[(int)FactMaterials.Hint]; Solution = materials[(int)FactMaterials.Solution]; } #if UNITY_EDITOR // not working if (EditorApplication.isPlaying) return; foreach (Collider collider in transform.GetComponentsInChildren<Collider>(includeInactive: true)) { if (!collider.gameObject.GetComponent<FactObject>()) try { collider.gameObject.AddComponent<FactObject>(); } catch(Exception) { } } #endif } void ISerializationCallbackReceiver.OnAfterDeserialize() { { materials = new Material[4]; materials[(int)FactMaterials.Default] = Default; materials[(int)FactMaterials.Selected] = Selected; materials[(int)FactMaterials.Hint] = Hint; materials[(int)FactMaterials.Solution] = Solution; } } #endregion private void Awake() { AllChildren = transform.GetComponentsInChildren<FactObject>(includeInactive: true).ToList(); AllChildren.Remove(this); } protected override void FactUpdated() { Awake(); ReLabel(); foreach (FactObject childObject in AllChildren) { childObject._URI = _URI; childObject._Fact = null; childObject.ReLabel(); } } public void ReLabel() { string[] mother_child_labels = new[] { URI }.ShallowCloneAppend(Fact.DependentFactIds) .Select(fid => FactOrganizer.AllFacts.TryGetValue(fid, out Fact fact) ? fact.Label : "N/A") .ToArray(); string indexoverflow = "{" + mother_child_labels.Length.ToString() + "}"; for (int i = 0; i < Math.Min(FactText.Count(), StringLabelFormats.Count()); i++) { if (StringLabelFormats[i].Contains(indexoverflow)) { Debug.LogWarning("Format contains illegal Argument and will be ignored: " + indexoverflow); continue; } FactText[i].text = string.Format(StringLabelFormats[i], mother_child_labels); } switch (Fact) // for highly customized labels { case TestFact testFact: //FactText[0].text = testFact.Label; break; default: break; } } public void CascadeForMeAndChildren(Action<FactObject> func) { func(this); foreach (FactObject fo in AllChildren) func(fo); } public void ForAllRenderer(Action<Renderer> func) { foreach (Renderer ren in renderer) func(ren); } public void CoroutineCascadeForMeAndChildrenAllRenderer(Func<FactObject, Renderer, IEnumerator> func) { this.StopAllCoroutines(); CascadeForMeAndChildren((FactObject fo) => fo.ForAllRenderer((Renderer ren) => StartCoroutine(func(fo, ren)) )); } }