From 6aaa4453247677dee03a4dd561ffc631225ce93b Mon Sep 17 00:00:00 2001 From: Bjoern Esswein <692-bessw@users.noreply.gl.kwarc.info> Date: Wed, 17 Jul 2024 18:11:01 +0200 Subject: [PATCH] WIP: refactor ScrollDetails into model (ActiveScroll.cs) and view (ScrollDetails.cs) --- .../Prefabs/UI/Facts/Factscreen.prefab | 2 +- Assets/Resources/Prefabs/UI/FrameITUI.prefab | 8 +- .../Prefabs/UI/Ingame/FrameITUI_mobile.prefab | 2 +- .../Prefabs/UI/Ingame/HidingCanvas.prefab | 6 +- .../InteractionEngine/CommunicationEvents.cs | 11 +- .../FactWrapper/RenderedScrollFact.cs | 22 +- .../Scripts/InteractionEngine/ShinyThings.cs | 6 +- Assets/Scripts/InventoryStuff/ActiveScroll.cs | 535 ++++++++++++++++++ .../InventoryStuff/ActiveScroll.cs.meta | 11 + .../Scripts/InventoryStuff/DisplayScrolls.cs | 5 +- .../InventoryStuff/ScrollClickedScript.cs | 3 +- .../Scripts/InventoryStuff/ScrollDetails.cs | 446 --------------- .../UI/FactExplorer/OpenFactExplorer.cs | 2 +- Assets/Scripts/UI/InGame/PopupBehavior.cs | 30 +- Assets/Scripts/UI/InGame/ScrollDetails.cs | 111 ++++ .../InGame}/ScrollDetails.cs.meta | 0 Assets/Scripts/UI/InGame/ScrollView.cs | 5 + Assets/Scripts/UI/InGame/ScrollView.cs.meta | 11 + Assets/Scripts/UI/InGame/WebViewController.cs | 2 +- 19 files changed, 734 insertions(+), 484 deletions(-) create mode 100644 Assets/Scripts/InventoryStuff/ActiveScroll.cs create mode 100644 Assets/Scripts/InventoryStuff/ActiveScroll.cs.meta delete mode 100644 Assets/Scripts/InventoryStuff/ScrollDetails.cs create mode 100644 Assets/Scripts/UI/InGame/ScrollDetails.cs rename Assets/Scripts/{InventoryStuff => UI/InGame}/ScrollDetails.cs.meta (100%) create mode 100644 Assets/Scripts/UI/InGame/ScrollView.cs create mode 100644 Assets/Scripts/UI/InGame/ScrollView.cs.meta diff --git a/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab b/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab index 8addc4b7..9dff07b6 100644 --- a/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab +++ b/Assets/Resources/Prefabs/UI/Facts/Factscreen.prefab @@ -507,7 +507,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -180} + m_AnchoredPosition: {x: 0, y: 0.000030517578} m_SizeDelta: {x: 0, y: 0} m_Pivot: {x: 0.5, y: 1} --- !u!114 &8823539307371861913 diff --git a/Assets/Resources/Prefabs/UI/FrameITUI.prefab b/Assets/Resources/Prefabs/UI/FrameITUI.prefab index 65acbc15..0a25a451 100644 --- a/Assets/Resources/Prefabs/UI/FrameITUI.prefab +++ b/Assets/Resources/Prefabs/UI/FrameITUI.prefab @@ -406,12 +406,12 @@ PrefabInstance: - target: {fileID: 8553388048532215990, guid: 884ac57de337c364391b247761071fb1, type: 3} propertyPath: m_AnchorMax.y - value: 0 + value: 1 objectReference: {fileID: 0} - target: {fileID: 8553388048532215990, guid: 884ac57de337c364391b247761071fb1, type: 3} propertyPath: m_AnchorMin.y - value: 0 + value: 1 objectReference: {fileID: 0} - target: {fileID: 8553388048532215990, guid: 884ac57de337c364391b247761071fb1, type: 3} @@ -556,7 +556,7 @@ PrefabInstance: - target: {fileID: 6500467619489830996, guid: 292834880e6f0e54186b873acc62d3f2, type: 3} propertyPath: m_AnchoredPosition.y - value: 443880 + value: 506520 objectReference: {fileID: 0} - target: {fileID: 7849991042685492731, guid: 292834880e6f0e54186b873acc62d3f2, type: 3} @@ -606,7 +606,7 @@ PrefabInstance: - target: {fileID: 7989559431199338490, guid: 292834880e6f0e54186b873acc62d3f2, type: 3} propertyPath: m_AnchoredPosition.y - value: -24660 + value: -28140 objectReference: {fileID: 0} - target: {fileID: 8004702056544321748, guid: 292834880e6f0e54186b873acc62d3f2, type: 3} diff --git a/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab b/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab index bce53f30..4f103c2a 100644 --- a/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab +++ b/Assets/Resources/Prefabs/UI/Ingame/FrameITUI_mobile.prefab @@ -4952,7 +4952,7 @@ PrefabInstance: - target: {fileID: 7989559431199338490, guid: 292834880e6f0e54186b873acc62d3f2, type: 3} propertyPath: m_AnchoredPosition.y - value: -1700 + value: -2000 objectReference: {fileID: 0} - target: {fileID: 8004702056544321748, guid: 292834880e6f0e54186b873acc62d3f2, type: 3} diff --git a/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab b/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab index ec56a279..e1fc2e4b 100644 --- a/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab +++ b/Assets/Resources/Prefabs/UI/Ingame/HidingCanvas.prefab @@ -2464,9 +2464,6 @@ MonoBehaviour: parameterDisplayPrefab: {fileID: 8358525157842135574, guid: 0651df442e07acf439dd439c86c20e93, type: 3} mmtAnswerPopUp: {fileID: 6618856106128302243} - currentMmtAnswer: - DynamicScrollDescriptionsActive: 1 - AutomaticHintGenerationActive: 1 --- !u!114 &8004702057798297409 MonoBehaviour: m_ObjectHideFlags: 0 @@ -3036,7 +3033,6 @@ MonoBehaviour: ScrollButtons: [] ScrollPrefab: {fileID: 3173330253721512196, guid: a6a9a3ebdb022e546a21d9f9ff148261, type: 3} - DetailScreen: {fileID: 8004702057798297436} scrollscreenContent: {fileID: 6500467619489830996} --- !u!114 &3490402746730127523 MonoBehaviour: @@ -3711,7 +3707,7 @@ PrefabInstance: - target: {fileID: 4838871000058222821, guid: 49deb83b881477047bfac0ee629a7ae9, type: 3} propertyPath: m_AnchoredPosition.y - value: -0.000045776367 + value: -0.000030517578 objectReference: {fileID: 0} m_RemovedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 49deb83b881477047bfac0ee629a7ae9, type: 3} diff --git a/Assets/Scripts/InteractionEngine/CommunicationEvents.cs b/Assets/Scripts/InteractionEngine/CommunicationEvents.cs index bca75d87..f9b0f0f3 100644 --- a/Assets/Scripts/InteractionEngine/CommunicationEvents.cs +++ b/Assets/Scripts/InteractionEngine/CommunicationEvents.cs @@ -13,20 +13,15 @@ public static class CommunicationEvents public static UnityEvent<Fact> AddFactEvent = new(); public static UnityEvent<Fact> RemoveFactEvent = new(); - public static UnityEvent<REST_JSON_API.ScrollApplicationCheckingError[]> ScrollApplicationCheckingErrorEvent = new(); - public static UnityEvent PushoutFactFailEvent = new(); - public static UnityEvent gameSucceededEvent = new(); public static UnityEvent gameNotSucceededEvent = new(); - public static UnityEvent NewAssignmentEvent = new(); public static UnityEvent StartT0Event = new(); - - public static UnityEvent<string> ScrollFactHintEvent = new(); + /** <summary>Inform ui that it should animate the given fact with the given material.</summary> */ public static UnityEvent<string, FactWrapper.FactMaterials> AnimateExistingFactEvent = new(); + /** <summary>Start firework and invoke AnimateExistingFactEvent.</summary> */ public static UnityEvent<Fact, FactWrapper.FactMaterials> AnimateExistingAsSolutionEvent = new(); + /** <summary>Show a missing fact for a short time as hint for the user.</summary> */ public static UnityEvent<Fact> AnimateNonExistingFactEvent = new(); - public static UnityEvent<List<string>> HintAvailableEvent = new(); - //------------------------------------------------------------------------------------ diff --git a/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs b/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs index 6a362b1c..a52dff62 100644 --- a/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs +++ b/Assets/Scripts/InteractionEngine/FactHandling/FactWrapper/RenderedScrollFact.cs @@ -1,5 +1,6 @@ using REST_JSON_API; using System.Collections.Generic; +using System.Linq; using TMPro; using UnityEngine; using UnityEngine.EventSystems; @@ -66,7 +67,6 @@ protected override void FactUpdated() if (VerboseURI) Debug.Log(nameof(RenderedScrollFact) + " recieved Fact: " + URI); - NewAssignmentEvent.Invoke(); ResetPayload(); base.FactUpdated(); } @@ -97,13 +97,15 @@ protected override void _DeleteFactEvent(Fact fact) protected override void _OnEnable() { - HintAvailableEvent.AddListener(OnHintAvailable); + ActiveScroll.HintAvailableEvent.AddListener(OnHintAvailable); + ActiveScroll.OnScrollDynamicInfoUpdated.AddListener(OnScrollUpdated); ResetPayload(); } protected override void _OnDisable() { - HintAvailableEvent.RemoveListener(OnHintAvailable); + ActiveScroll.HintAvailableEvent.RemoveListener(OnHintAvailable); + ActiveScroll.OnScrollDynamicInfoUpdated.RemoveListener(OnScrollUpdated); } public void Populate(Scroll scroll, string scroll_fact_uri) @@ -120,12 +122,22 @@ private void SetLabel(string label) LabelMesh.text = label ?? Scroll?.requiredFacts[ID].label ?? "Err"; } + private void OnScrollUpdated(Scroll rendered) { + // this if has been copied during refactoring, I don't know if we need it^^ + if(ActiveScroll.Instance.DynamicScrollDescriptionsActive && + rendered.requiredFacts.Any(rf => rf.@ref.uri == ScrollFactURI)) + { + // Update ScrollParameter label (side effect of setting the property "Scroll") + Scroll = rendered; + } + } + public void OnClickHintButton() { - ScrollFactHintEvent.Invoke(URI); + ActiveScroll.Instance.ButtonClicked(new HintScrollButton(URI)); } - public void OnHintAvailable(List<string> uris) + private void OnHintAvailable(IReadOnlyList<string> uris) { HintButton.SetActive(uris.Contains(ScrollFactURI)); } diff --git a/Assets/Scripts/InteractionEngine/ShinyThings.cs b/Assets/Scripts/InteractionEngine/ShinyThings.cs index 37993ee7..cb0ff215 100644 --- a/Assets/Scripts/InteractionEngine/ShinyThings.cs +++ b/Assets/Scripts/InteractionEngine/ShinyThings.cs @@ -28,13 +28,13 @@ public GameObject private void OnEnable() { - CommunicationEvents.PushoutFactFailEvent.AddListener(LetItRain); + ActiveScroll.OnScrollApplicationError.AddListener(LetItRain); CommunicationEvents.AnimateExistingAsSolutionEvent.AddListener(HighlightWithFireworks); } private void OnDisable() { - CommunicationEvents.PushoutFactFailEvent.RemoveListener(LetItRain); + ActiveScroll.OnScrollApplicationError.RemoveListener(LetItRain); CommunicationEvents.AnimateExistingAsSolutionEvent.RemoveListener(HighlightWithFireworks); } @@ -117,7 +117,7 @@ IEnumerator _BlossomAndDie() } } - public void LetItRain() + public void LetItRain(REST_JSON_API.ScrollApplicationCheckingError[] _) { // check if couroutine is waiting or finished if (!rain_wait.MoveNext() || !rain.MoveNext()) diff --git a/Assets/Scripts/InventoryStuff/ActiveScroll.cs b/Assets/Scripts/InventoryStuff/ActiveScroll.cs new file mode 100644 index 00000000..67fc90f7 --- /dev/null +++ b/Assets/Scripts/InventoryStuff/ActiveScroll.cs @@ -0,0 +1,535 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; +using REST_JSON_API; +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.Networking; + +#nullable enable annotations + +/// <summary> +/// This class is a singleton that keeps track of the currently active scroll and comunicates with the FrameIT Server. +/// It represents the model of the Model-View-Controler pattern. +/// </summary> +public class ActiveScroll : MonoBehaviour +{ + #region static part + + /// <summary> + /// Static property to access the singleton instance of ActiveScroll + /// </summary> + public static ActiveScroll Instance + { + get => _Instance; + set + { + if (_Instance != value) + Destroy(_Instance); + + _Instance = value; + } + } + private static ActiveScroll _Instance; + + /** <summary>Event that is invoked when a scroll button is clicked, e.g. magic button. You can use it to handle custom buttons.</summary> */ + public static UnityEvent<ScrollButton> OnButtonClick = new(); + /** <summary>Notifies about availible hints.</summary> */ + public static UnityEvent<IReadOnlyList<string>> HintAvailableEvent = new(); + /** <summary>Event that is invoked when a fact has been assigned to a slot and the ui needs to update.</summary> */ + public static UnityEvent<string,SlotAssignment> OnFactAssignmentUpdated = new(); + /** <summary>Event that is invoked when the MMT server returned an error.</summary> */ + public static UnityEvent<string> OnMMTServerComunicationError = new(); + /** <summary>Event that is invoked when the scroll dynamic info returned errors in the current scroll assignment.</summary> */ + public static UnityEvent<ScrollApplicationCheckingError[]> OnScrollDynamicInfoError = new(); + /** <summary>Event that is invoked when the scroll application returned errors in the current assignment.</summary> */ + public static UnityEvent<ScrollApplicationCheckingError[]> OnScrollApplicationError = new(); + /** <summary>Event that is invoked when the OnMMTServerError or OnScrollApplicationFailed error state has been resolved and e.g. the error popup can be cloesed.</summary> */ + public static UnityEvent OnCancelErrorState = new(); + /** <summary>Event that is invoked when the scroll has been solved.</summary> */ + public static UnityEvent<IReadOnlyList<Fact>> OnScrollSolved = new(); + /** <summary>Event that is invoked when an other scroll has been selected.</summary> */ + public static UnityEvent<ActiveScroll> OnScrollChanged = new(); + /** <summary>Event that is invoked when the dynamic info of the scroll has been updated e.g. because a fact has been assigned.</summary> */ + public static UnityEvent<Scroll> OnScrollDynamicInfoUpdated = new(); + + void Awake() + { + Instance = this; + } + private void OnDestroy() + { + _Instance = null; + } + + + /// <summary> + /// Creates a new instance and sets the scroll + /// </summary> + public static void SetScroll(Scroll scroll_to_set) + { + Instance = new ActiveScroll + { + Scroll = scroll_to_set + }; + // notify the observers that the scroll has changed + OnScrollChanged.Invoke(Instance); + } + + #endregion static part + + + public bool DynamicScrollDescriptionsActive = true; + public bool AutomaticHintGenerationActive = true; + + private readonly IReadOnlyList<string> NoDynamicScroll = new List<string>() + { + // Insert ScrollURIS that are poorly optimized + }; + + + /// <summary> + /// Variable used to block that only one SendView is running at a time. + /// </summary> + private bool SendingViewDone = true; + /// <summary> + /// this is true while a fact assignment is enqueued + /// </summary> + private bool DynamicScrollInQue = false; + /// <summary> + /// this is true while a scroll application via magic button is enqueued + /// </summary> + private bool MagicInQue = false; + private string currentMmtAnswer; + + /** <summary>List of available hints for the current partial assignment.</summary> */ + private List<Fact> LatestRenderedHints; + /** <summary>List of available auto comletions for the current partial assignment. Used to generate hints.</summary> */ + private List<ScrollAssignment> LatestBackwartsCompletions; + + private Scroll _Scroll; + /// <summary> + /// The currently active scroll + /// </summary> + public Scroll Scroll + { + get => _Scroll; + private set { + _Scroll = value; + + // initialize the assignments dictionary with the references to the required fact slots as keys + // and an empty SlottAssignment + _Assignments = value.requiredFacts + .ToDictionary(fact => fact.@ref.uri, _ => new SlotAssignment()); + + // apply fact assignments that are pre defined by the stage (defaults or constrains), if there are any + if (StageStatic.stage.solution.ScrollOverwrites + .TryGetValue(value.ScrollReference, out (string, int, bool)[] population) + && population.Length > 0) + { + foreach ((string Id, int index, bool show) in population) + { + var key = value.requiredFacts[index].@ref.uri; + _Assignments[key].fact = FactRecorder.AllFacts[Id]; + _Assignments[key].IsVisible = show; + } + } + } + } + /// <summary> + /// The currently active scroll with rendered description and backward_completions (hints) + /// </summary> + public Scroll RenderedScroll { get; private set; } + + public class SlotAssignment + { + public Fact? fact = null; + public bool IsVisible = true; + public bool IsSet => fact is not null; + } + + /// <summary> + /// Maps the required facts (keys) to the assigned facts (values) + /// </summary> + private Dictionary<string, SlotAssignment> _Assignments = new(); + + /// <summary> + /// Maps the required facts (keys) to the assigned facts (values). + /// </summary> + public IReadOnlyDictionary<string, SlotAssignment> Assignments => _Assignments; + + /// <summary> + /// The ScrollView should call this method when a button is clicked + /// </summary> + public void ButtonClicked(ScrollButton button) + { + switch (button) + { + // Magic button to apply the scroll + case MagicScrollButton: + if ((Scroll is not null) && ! MagicInQue) StartCoroutine(SendScrollApplication()); + break; + case HintScrollButton: + HintButtonPressed((HintScrollButton)button); + break; + // add cases for future additional buttons here + } + // notify observers that a button was clicked + OnButtonClick.Invoke(button); + } + + /// <summary> + /// Assigns the given fact to the given slotUri and calls the OnFactAssignmentUpdated Event + /// </summary> + /// <param name="slotUri"></param> + /// <param name="fact"></param> + /// <exception cref="ArgumentException"></exception> + public void AssignFact(string slotUri, Fact fact) + { + if (Assignments.ContainsKey(slotUri)) + { + var slot = _Assignments[slotUri]; + slot.fact = fact; + OnFactAssignmentUpdated.Invoke(slotUri, slot); + SendCurrentFactAssignments(); + } else { + throw new ArgumentException($"Can't assign fact, the given slot doesn't exist on the current scroll: '{slotUri}'"); + } + } + + /// <summary> + /// If the fact is not already assigned, assign it to the next empty slot, otherwise unassign it. + /// And calls the OnFactAssignmentUpdated Event. + /// </summary> + /// <param name="fact"></param> + /// <returns></returns> + public string? AssignOrRemoveFactToNextEmptySlot(Fact fact) + { + // unassign the fact if it is already assigned and return + try { + var assignedSlot = _Assignments.First(pair => pair.Value.fact == fact).Key; + AssignFact(assignedSlot, null); + return null; + } catch (Exception) { + // the fact has not been assigned yet => continue + } + + // find the first empty slot and assign the fact to it + try { + KeyValuePair<string, SlotAssignment> emptySlot = _Assignments.First(pair => pair.Value.IsSet); + AssignFact(emptySlot.Key, fact); + return emptySlot.Key; + } catch (Exception) { + return null; + } + } + + + #region MagicButton + private IEnumerator SendScrollApplication() + { + // there is already a MagicButton coroutine waiting => exit + if (MagicInQue) yield break; + + MagicInQue = true; + // wait if SendView is not yet done or dynamic scroll is in the queue + while (!SendingViewDone || DynamicScrollInQue) + yield return null; + MagicInQue = false; + + yield return SendView("/scroll/apply"); + + if (currentMmtAnswer == null) + { + // OnMMTServerComunicationError is already invoked in SendView + Debug.LogError("Magic FAILED"); + yield break; + } + else + { + if (CommunicationEvents.VerboseURI) + Debug.Log("Magic answers:\n" + currentMmtAnswer); + + System.DateTime deserializeTime = System.DateTime.UtcNow; + ScrollApplicationInfo scrollApplication = JsonConvert.DeserializeObject<ScrollApplicationInfo>(currentMmtAnswer); + Debug.LogFormat($"Answerd deserialized in : {(System.DateTime.UtcNow - deserializeTime).TotalMilliseconds}ms"); + + if (scrollApplication.acquiredFacts.Count == 0 + || scrollApplication.errors.Length > 0) + { + OnScrollApplicationError.Invoke(scrollApplication.errors); + } + else + { + OnCancelErrorState.Invoke(); + } + + yield return __GenerateUnityFactsForAcquiredMMTFacts(scrollApplication.acquiredFacts); + } + } + + private IEnumerator __GenerateUnityFactsForAcquiredMMTFacts(List<MMTFact> pushoutFacts) + { + List<Fact> new_facts = new(); + Dictionary<string, string> old_to_new = new(); + System.DateTime parseTime = System.DateTime.UtcNow; + + bool samestep = false; + for (int i = 0; i < pushoutFacts.Count; i++) + { + List<Fact> new_list = Fact.MMTFactory(pushoutFacts[i].MapURIs(old_to_new)); + + if (new_list.Count == 0) + { + Debug.LogWarning("Parsing on pushout-fact returned empty List -> One of the dependent facts does not exist or parsing failed"); + continue; + } + + foreach (Fact new_fact in new_list) + { + Fact added = FactAdder.AddFactIfNotFound(new_fact, out bool exists, samestep, null, ActiveScroll.Instance.Scroll.label); + if (!exists) + { + new_facts.Add(added); + CommunicationEvents.AnimateExistingFactEvent.Invoke(added.Id, FactWrapper.FactMaterials.Solution); + samestep = true; + } + else + { + // AnimateExistingFactEvent.Invoke(_new, FactWrapper.FactMaterials.Hint); // Automaticly done in FactRecorder + old_to_new.Add(new_fact.Id, added.Id); + } + } + + //yield return null; + } + + Debug.Log($"Facts parsed within {(System.DateTime.UtcNow - parseTime).TotalMilliseconds}ms"); + OnScrollSolved.Invoke(new_facts); + yield break; + } + + #endregion MagicButton + + #region HintButton + + private void HintButtonPressed(HintScrollButton hintButton) + { + if (FactRecorder.AllFacts.ContainsKey(hintButton.SlotUri)) + { + CommunicationEvents.AnimateExistingFactEvent.Invoke( + hintButton.SlotUri, + FactWrapper.FactMaterials.Hint + ); + } + + Fact hintFact = LatestRenderedHints.Find(x => x.Id == hintButton.SlotUri); // "Dictionary" + + ScrollAssignment suitableCompletion = + LatestBackwartsCompletions.Find((ScrollAssignment x) => x.fact.uri == hintButton.SlotUri); // "Dictionary" + + if (suitableCompletion != null && suitableCompletion.assignment is OMS assignment) + { + if (FactRecorder.AllFacts.ContainsKey(assignment.uri)) + { + CommunicationEvents.AnimateExistingFactEvent.Invoke( + assignment.uri, + FactWrapper.FactMaterials.Hint + ); + } + } + else if (hintFact != null) + { + if (FactRecorder.FindEquivalent(StageStatic.stage.factState.MyFactSpace, hintFact, out string found_key, out Fact _, out bool _, false)) + // existing fact -> Animate that + CommunicationEvents.AnimateExistingFactEvent.Invoke( + found_key, + FactWrapper.FactMaterials.Hint + ); + + else + { // Generate new FactRepresentation and animate it + CommunicationEvents.AnimateNonExistingFactEvent.Invoke(hintFact); + CommunicationEvents.AnimateExistingFactEvent.Invoke( + hintButton.SlotUri, + FactWrapper.FactMaterials.Hint + ); + } + } + } + #endregion HintButton + + #region FactAssignment + + public void SendCurrentFactAssignments() + { + if (Scroll?.ScrollReference == null + || NoDynamicScroll.Contains(Scroll.ScrollReference)) + return; + + if (AutomaticHintGenerationActive || DynamicScrollDescriptionsActive) + StartCoroutine(_SendAssignments()); + } + + private IEnumerator _SendAssignments() + { + if (DynamicScrollInQue) + yield break; // only need next in que to finish + + DynamicScrollInQue = true; + while (!SendingViewDone) + yield return null; // if we dont wait => server will crash + DynamicScrollInQue = false; + + yield return SendView("/scroll/dynamic"); + + if (currentMmtAnswer == null) + { + // OnMMTServerComunicationError is already invoked in SendView + Debug.LogError("Dynamic Scroll FAILED"); + yield break; + } + else + { + ScrollDynamicInfo scrollDynamicInfo; + try + { + scrollDynamicInfo = JsonConvert.DeserializeObject<ScrollDynamicInfo>(currentMmtAnswer); + //scrollDynamicInfo = IJSONsavable<ScrollDynamicInfo>.postprocess(scrollDynamicInfo); // DON'T! will remove hints + } + catch (JsonSerializationException ex) + { + Debug.LogException(ex); + Debug.LogError("Could not Deserialize MMT aswer for /scroll/dynamic\n" + currentMmtAnswer); + OnMMTServerComunicationError.Invoke("MMT Server communication error: could not deserialize message."); + yield break; + } + + ScrollApplicationCheckingError[] errors = scrollDynamicInfo.errors + .Where(err => err.kind != "nonTotal") // expected + .ToArray(); + + if (errors.Length > 0) + OnScrollDynamicInfoError.Invoke(errors); + + _ProcessScrollDynamicInfo(scrollDynamicInfo); + } + } + + private void _ProcessScrollDynamicInfo(ScrollDynamicInfo scrollDynamicInfo) + { + //TODO: more hints available in scrollDynamicInfo.rendered.requiredFacts + + LatestBackwartsCompletions = scrollDynamicInfo.backward_completions.Count > 0 + ? scrollDynamicInfo.backward_completions[0] + : new List<ScrollAssignment>(); + + List<string> hintUris = LatestBackwartsCompletions + .Select(completion => completion.fact.uri) + .ToList(); + + //Update Scroll, process data for later hints and update Uri-List for which hints are available + _processRenderedScroll(scrollDynamicInfo.rendered, hintUris); + + if (AutomaticHintGenerationActive) + //Show that Hint is available for ScrollParameter + HintAvailableEvent.Invoke(hintUris); + + return; + + void _processRenderedScroll(Scroll rendered, List<string> hintUris) + { + OnScrollDynamicInfoUpdated.Invoke(rendered); + + LatestRenderedHints = new(); + foreach (var requiredFact in rendered.requiredFacts) + { + //If ScrollFact is not assigned -> enable hint button + if (Assignments.TryGetValue(requiredFact.@ref.uri, out SlotAssignment assignedFact) && ! assignedFact.IsSet) + { + List<Fact> HintFactList = Fact.MMTFactory(requiredFact); + + foreach (Fact HintFact in HintFactList) + { + hintUris.Add(HintFact.Id); // == rendered.requiredFacts[i].@ref.uri + LatestRenderedHints.Add(HintFact); + } + } + } + + return; + } + } + + + #endregion FactAssignment + + /// <summary> + /// Only one instance of the SendView coroutine should run at a time. + /// </summary> + private IEnumerator SendView(string endpoint) + { + SendingViewDone = false; + + //while (ParameterDisplays == null) // Wait for server + // yield return null; + + string body = prepareScrollAssignments(); + + using UnityWebRequest www = UnityWebRequest.Put(CommunicationEvents.ServerAdress + endpoint, body); + www.method = UnityWebRequest.kHttpVerbPOST; + www.SetRequestHeader("Content-Type", "application/json"); + + System.DateTime sendTime = System.DateTime.UtcNow; + yield return www.SendWebRequest(); + //if (VerboseURI) + Debug.LogFormat("Server answerd in : {0}ms" + , (System.DateTime.UtcNow - sendTime).TotalMilliseconds); + + if (www.result == UnityWebRequest.Result.ConnectionError + || www.result == UnityWebRequest.Result.ProtocolError) + { + Debug.LogWarning(www.error); + currentMmtAnswer = null; + OnMMTServerComunicationError.Invoke($"Server communication error: {www.error}"); + SendingViewDone = true; + yield break; + } + else + { + while (!www.downloadHandler.isDone) + yield return null; + + currentMmtAnswer = www.downloadHandler.text + .Replace("\"float\":null", "\"float\":0.0"); // cannot convert null to value type + } + + SendingViewDone = true; + yield break; + + /// <summary>Maps the assigned slots to ScrollAssignment objects. + /// Returns json serialized ScrollApplication containing the list of ScrollAssignments.</summary> + string prepareScrollAssignments() + { + List<ScrollAssignment> assignmentList = Assignments.Where(assignment => assignment.Value is not null) + .Select(assignment => new ScrollAssignment(assignment.Key, assignment.Value.fact.ScalaFact.@ref)) + .ToList(); + + return JsonConvert.SerializeObject(new ScrollApplication(Scroll.ScrollReference, assignmentList)); + } + } +} + +/// <summary> +/// Parrent class for all scroll buttons, inherit from this class to implement custom buttons. +/// </summary> +public abstract class ScrollButton {} + +public class MagicScrollButton : ScrollButton {} +public class HintScrollButton : ScrollButton +{ + public readonly string SlotUri; + public HintScrollButton(string slotUri) => SlotUri = slotUri; +} \ No newline at end of file diff --git a/Assets/Scripts/InventoryStuff/ActiveScroll.cs.meta b/Assets/Scripts/InventoryStuff/ActiveScroll.cs.meta new file mode 100644 index 00000000..9098ea19 --- /dev/null +++ b/Assets/Scripts/InventoryStuff/ActiveScroll.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ae310511ad607a64e891bcb46517277b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/InventoryStuff/DisplayScrolls.cs b/Assets/Scripts/InventoryStuff/DisplayScrolls.cs index a9ec4ddc..2ce0a98a 100644 --- a/Assets/Scripts/InventoryStuff/DisplayScrolls.cs +++ b/Assets/Scripts/InventoryStuff/DisplayScrolls.cs @@ -10,7 +10,6 @@ public class DisplayScrolls : MonoBehaviour static public List<REST_JSON_API.Scroll> AllowedScrolls; public GameObject[] ScrollButtons; public GameObject ScrollPrefab; - public GameObject DetailScreen; public Transform scrollscreenContent; @@ -35,13 +34,11 @@ void BuildScrollGUI() { var obj = Instantiate(ScrollPrefab, scrollscreenContent); obj.GetComponent<ScrollClickedScript>().scroll = AllowedScrolls[i]; - obj.GetComponent<ScrollClickedScript>().DetailScreen = this.DetailScreen; obj.transform.GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = AllowedScrolls[i].label; ScrollButtons[i] = obj; } REST_JSON_API.Scroll preferredStartScroll = AllowedScrolls.Find(x => x.label.Equals(preferredStartScrollName)); - if (preferredStartScroll != null) - ScrollDetails.Instance?.SetScroll(preferredStartScroll); + if (preferredStartScroll != null) ActiveScroll.SetScroll(preferredStartScroll); } } diff --git a/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs b/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs index 50ee8f5c..45c8f5b2 100644 --- a/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs +++ b/Assets/Scripts/InventoryStuff/ScrollClickedScript.cs @@ -5,10 +5,9 @@ public class ScrollClickedScript : MonoBehaviour, IPointerDownHandler { public Scroll scroll; - public GameObject DetailScreen; public void OnPointerDown(PointerEventData eventData) { - this.DetailScreen.GetComponent<ScrollDetails>().SetScroll(this.scroll); + ActiveScroll.SetScroll(this.scroll); } } diff --git a/Assets/Scripts/InventoryStuff/ScrollDetails.cs b/Assets/Scripts/InventoryStuff/ScrollDetails.cs deleted file mode 100644 index 63850233..00000000 --- a/Assets/Scripts/InventoryStuff/ScrollDetails.cs +++ /dev/null @@ -1,446 +0,0 @@ -using Newtonsoft.Json; -using REST_JSON_API; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using TMPro; -using UnityEngine; -using UnityEngine.Networking; -using static CommunicationEvents; - -public class ScrollDetails : MonoBehaviour -{ - public static ScrollDetails Instance - { - get => _Instance; - set - { - if (_Instance != value) - Destroy(_Instance); - - _Instance = value; - } - } - private static ScrollDetails _Instance; - - public WorldCursor cursor; - public GameObject parameterDisplayPrefab; - public static Scroll ActiveScroll { get; private set; } - public GameObject mmtAnswerPopUp; - private PopupBehavior Popup; - - public static List<RenderedScrollFact> ParameterDisplays { get; private set; } - private static List<ScrollAssignment> LatestBackwartsCompletions; - private static List<Fact> LatestRenderedHints; - - public string currentMmtAnswer; - - public bool DynamicScrollDescriptionsActive = true; - public bool AutomaticHintGenerationActive = true; - - private bool SendingViewDone = true; - private bool DynamicScrollInQue = false; - private bool MagicInQue = false; - - private readonly IReadOnlyList<string> NoDynamicScroll = new List<string>() - { - // Insert ScrollURIS that are poorly optimized - }; - - void Awake() - { - Instance = this; - - if (cursor == null) - cursor = FindObjectOfType<WorldCursor>(); - - Popup = mmtAnswerPopUp.GetComponent<PopupBehavior>(); - Popup.gameObject.SetActive(true); // force Awake - } - - private void OnEnable() - { - ScrollFactHintEvent.AddListener(animateHint); - NewAssignmentEvent.AddListener(NewAssignmentTrigger); - } - - private void OnDisable() - { - ScrollFactHintEvent.RemoveListener(animateHint); - NewAssignmentEvent.RemoveListener(NewAssignmentTrigger); - } - - private void OnDestroy() - { - _Instance = null; - } - - public void SetScroll(Scroll scroll_to_set) - { - ActiveScroll = scroll_to_set; - - Transform originalScroll = gameObject.transform.GetChild(1); - Transform originalScrollView = originalScroll.GetChild(1); - Transform originalViewport = originalScrollView.GetChild(0); - - //Clear all current ScrollFacts - originalViewport.GetChild(0).gameObject.DestroyAllChildren(); - - originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = ActiveScroll.description; - - //ParameterDisplays.ForEach(gameObj => Destroy(gameObj)); - ParameterDisplays = new(); - for (int i = 0; i < ActiveScroll.requiredFacts.Count; i++) - { - GameObject originalObj = - Instantiate(parameterDisplayPrefab, originalViewport.GetChild(0)); - - RenderedScrollFact originalRSF = - originalObj.GetComponentInChildren<RenderedScrollFact>(); - - ParameterDisplays.Add(originalRSF); - - originalRSF.Populate(ActiveScroll, ActiveScroll.requiredFacts[i].@ref.uri); - } - - if (StageStatic.stage.solution.ScrollOverwrites - .TryGetValue(ActiveScroll.ScrollReference, out (string, int, bool)[] population) - && population.Length > 0) - { - DynamicScrollInQue = true; // block update on population - - foreach ((string Id, int index, bool show) in population) - { - ParameterDisplays[index].URI = Id; - ParameterDisplays[index].transform.parent.gameObject.SetActive(show); - } - - DynamicScrollInQue = false; // unblock - } - - NewAssignmentEvent.Invoke(); // init display - } - - public bool SetNextEmptyTo(FactObjectUI activator) - { - RenderedScrollFact check = ParameterDisplays - .Find(RSF => RSF != null - && RSF.Payload != null - && RSF.Payload.Equals(activator)); - - if (check != null) - { - check.URI = null; - return false; - } - - RenderedScrollFact empty = ParameterDisplays - .Find(RSF => !RSF.IsSet); - - if (empty == null) - return false; - - empty.SetByFactObject(activator); - return true; - } - - public void MagicButtonTrigger() - { - if (ActiveScroll == null) - return; - - StartCoroutine(_MagicButton()); - - IEnumerator _MagicButton() - { - if (MagicInQue) - yield break; // only need next in que to finish - - MagicInQue = true; - while (!SendingViewDone || DynamicScrollInQue) - yield return null; // Wait for last assignment - MagicInQue = false; - - yield return SendView("/scroll/apply"); - - if (currentMmtAnswer == null) - { - Debug.LogError("Magic FAILED"); - ScrollApplicationCheckingErrorEvent.Invoke(null); - } - else - { - if (VerboseURI) - Debug.Log("Magic answers:\n" + currentMmtAnswer); - - System.DateTime serializeTime = System.DateTime.UtcNow; - ScrollApplicationInfo pushout = JsonConvert.DeserializeObject<ScrollApplicationInfo>(currentMmtAnswer); - Debug.LogFormat($"Answerd serialized in : {(System.DateTime.UtcNow - serializeTime).TotalMilliseconds}ms"); - - if (pushout.acquiredFacts.Count == 0 - || pushout.errors.Length > 0) - { - ScrollApplicationCheckingErrorEvent.Invoke(pushout.errors); - PushoutFactFailEvent.Invoke(); - } - else - Popup.HidePopUp(); //close error Window - - yield return __GeneratePushoutFacts(pushout.acquiredFacts); - } - } - - IEnumerator __GeneratePushoutFacts(List<MMTFact> pushoutFacts) - { - List<Fact> new_facts = new(); - Dictionary<string, string> old_to_new = new(); - System.DateTime parseTime = System.DateTime.UtcNow; - - bool samestep = false; - for (int i = 0; i < pushoutFacts.Count; i++) - { - List<Fact> new_list = Fact.MMTFactory(pushoutFacts[i].MapURIs(old_to_new)); - - if (new_list.Count == 0) - { - Debug.LogWarning("Parsing on pushout-fact returned empty List -> One of the dependent facts does not exist or parsing failed"); - continue; - } - - foreach (Fact new_fact in new_list) - { - Fact added = FactAdder.AddFactIfNotFound(new_fact, out bool exists, samestep, null, ActiveScroll.label); - if (!exists) - { - new_facts.Add(added); - AnimateExistingFactEvent.Invoke(added.Id, FactWrapper.FactMaterials.Solution); - samestep = true; - } - else - { - // AnimateExistingFactEvent.Invoke(_new, FactWrapper.FactMaterials.Hint); // Automaticly done in FactRecorder - old_to_new.Add(new_fact.Id, added.Id); - } - } - - //yield return null; - } - - Debug.Log($"Facts parsed within {(System.DateTime.UtcNow - parseTime).TotalMilliseconds}ms"); - yield break; - } - } - - public void NewAssignmentTrigger() - { - //return; //if you read this, delete this line! - - if (ActiveScroll?.ScrollReference == null - || NoDynamicScroll.Contains(ActiveScroll.ScrollReference)) - return; - - if (AutomaticHintGenerationActive || DynamicScrollDescriptionsActive) - StartCoroutine(_NewAssignment()); - - IEnumerator _NewAssignment() - { - if (DynamicScrollInQue) - yield break; // only need next in que to finish - - DynamicScrollInQue = true; - while (!SendingViewDone) - yield return null; // if we dont wait => server will crash - DynamicScrollInQue = false; - - yield return SendView("/scroll/dynamic"); - - if (currentMmtAnswer == null) - { - Debug.LogError("Dynamic Scroll FAILED"); - } - else - { - ScrollDynamicInfo scrollDynamicInfo; - try - { - scrollDynamicInfo = JsonConvert.DeserializeObject<ScrollDynamicInfo>(currentMmtAnswer); - //scrollDynamicInfo = IJSONsavable<ScrollDynamicInfo>.postprocess(scrollDynamicInfo); // DON'T! will remove hints - } - catch (JsonSerializationException ex) - { - Debug.LogException(ex); - Debug.LogError("Could not Deserialize MMT aswer for /scroll/dynamic\n" + currentMmtAnswer); - yield break; - } - - ScrollApplicationCheckingError[] errors = scrollDynamicInfo.errors - .Where(err => err.kind != "nonTotal") // expected - .ToArray(); - - if (errors.Length > 0) - ScrollApplicationCheckingErrorEvent.Invoke(errors); - - processScrollDynamicInfo(scrollDynamicInfo); - } - } - } - - private IEnumerator SendView(string endpoint) - { - SendingViewDone = false; - - while (ParameterDisplays == null) // Wait for server - yield return null; - - string body = prepareScrollAssignments(); - - using UnityWebRequest www = UnityWebRequest.Put(ServerAdress + endpoint, body); - www.method = UnityWebRequest.kHttpVerbPOST; - www.SetRequestHeader("Content-Type", "application/json"); - - System.DateTime sendTime = System.DateTime.UtcNow; - yield return www.SendWebRequest(); - //if (VerboseURI) - Debug.LogFormat("Server answerd in : {0}ms" - , (System.DateTime.UtcNow - sendTime).TotalMilliseconds); - - if (www.result == UnityWebRequest.Result.ConnectionError - || www.result == UnityWebRequest.Result.ProtocolError) - { - Debug.LogWarning(www.error); - currentMmtAnswer = null; - } - else - { - while (!www.downloadHandler.isDone) - yield return null; - - currentMmtAnswer = www.downloadHandler.text - .Replace("\"float\":null", "\"float\":0.0"); // cannot convert null to value type - } - - SendingViewDone = true; - yield break; - - string prepareScrollAssignments() - { - List<ScrollAssignment> assignmentList = new(); - - for (int i = 0; i < ParameterDisplays.Count; i++) - { - Fact tempFact = ParameterDisplays[i].Fact; - if (tempFact != null) - assignmentList.Add(new ScrollAssignment(ActiveScroll.requiredFacts[i].@ref.uri, tempFact.Id)); - } - - return JsonConvert.SerializeObject(new ScrollApplication(ActiveScroll.ScrollReference, assignmentList)); - } - } - - private void processScrollDynamicInfo(ScrollDynamicInfo scrollDynamicInfo) - { - //TODO: more hints available in scrollDynamicInfo.rendered.requiredFacts - - LatestBackwartsCompletions = scrollDynamicInfo.backward_completions.Count > 0 - ? scrollDynamicInfo.backward_completions[0] - : new List<ScrollAssignment>(); - - List<string> hintUris = LatestBackwartsCompletions - .Select(completion => completion.fact.uri) - .ToList(); - - //Update Scroll, process data for later hints and update Uri-List for which hints are available - _processRenderedScroll(scrollDynamicInfo.rendered, hintUris); - - if (AutomaticHintGenerationActive) - //Show that Hint is available for ScrollParameter - HintAvailableEvent.Invoke(hintUris); - - return; - - void _processRenderedScroll(Scroll rendered, List<string> hintUris) - { - if (DynamicScrollDescriptionsActive) - { // Update scroll-description - Transform scroll = gameObject.transform.GetChild(1).transform; - scroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = rendered.description; - } - - LatestRenderedHints = new(); - for (int i = 0; i < rendered.requiredFacts.Count; i++) - { - RenderedScrollFact RenderedScrollFact = ParameterDisplays - .Find(RSF => RSF.ScrollFactURI == rendered.requiredFacts[i].@ref.uri); - - if (RenderedScrollFact == null) - { // e.g. FUNCs - //if(CommunicationEvents.VerboseScroll) - // Debug.Log($"Descrapancy between requiredFacts and displayed facts:" + - // $"Could not find display with ref: {rendered.requiredFacts[i].@ref.uri}"); - continue; - } - - if (DynamicScrollDescriptionsActive) - //Update ScrollParameter label - RenderedScrollFact.Scroll = rendered; - - //If ScrollFact is assigned -> No Hint - if (!RenderedScrollFact.IsSet) - { - List<Fact> HintFactList = Fact.MMTFactory(rendered.requiredFacts[i]); - - foreach (Fact HintFact in HintFactList) - { - hintUris.Add(HintFact.Id); // == rendered.requiredFacts[i].@ref.uri - LatestRenderedHints.Add(HintFact); - } - } - } - - return; - } - } - - public void animateHint(string scrollParameterUri) - { - if (FactRecorder.AllFacts.ContainsKey(scrollParameterUri)) - AnimateExistingFactEvent.Invoke( - scrollParameterUri, - FactWrapper.FactMaterials.Hint - ); - - Fact hintFact = LatestRenderedHints.Find(x => x.Id == scrollParameterUri); // "Dictionary" - - ScrollAssignment suitableCompletion = - LatestBackwartsCompletions.Find((ScrollAssignment x) => x.fact.uri == scrollParameterUri); // "Dictionary" - - if (suitableCompletion != null && suitableCompletion.assignment is OMS assignment) - { - if (FactRecorder.AllFacts.ContainsKey(assignment.uri)) - { - AnimateExistingFactEvent.Invoke( - assignment.uri, - FactWrapper.FactMaterials.Hint - ); - } - } - else if (hintFact != null) - { - if (FactRecorder.FindEquivalent(StageStatic.stage.factState.MyFactSpace, hintFact, out string found_key, out Fact _, out bool _, false)) - // existing fact -> Animate that - AnimateExistingFactEvent.Invoke( - found_key, - FactWrapper.FactMaterials.Hint - ); - - else - { // Generate new FactRepresentation and animate it - AnimateNonExistingFactEvent.Invoke(hintFact); - AnimateExistingFactEvent.Invoke( - scrollParameterUri, - FactWrapper.FactMaterials.Hint - ); - } - } - } -} diff --git a/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs b/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs index 4f1afd71..ff1434fe 100644 --- a/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs +++ b/Assets/Scripts/UI/FactExplorer/OpenFactExplorer.cs @@ -109,7 +109,7 @@ private void DoOpenFactExplorer() } private bool DoSetActive() - => ScrollDetails.Instance.SetNextEmptyTo(CachedFactWrapper as FactObjectUI); + => ActiveScroll.Instance.AssignOrRemoveFactToNextEmptySlot(CachedFactWrapper.Fact) is not null; #endregion Implementation } diff --git a/Assets/Scripts/UI/InGame/PopupBehavior.cs b/Assets/Scripts/UI/InGame/PopupBehavior.cs index 6685ced2..6f1826ff 100644 --- a/Assets/Scripts/UI/InGame/PopupBehavior.cs +++ b/Assets/Scripts/UI/InGame/PopupBehavior.cs @@ -24,10 +24,25 @@ public string MessageText private string errorMessage; #region UnityMethods - void Awake() + + void OnEnable() { - CommunicationEvents.ScrollApplicationCheckingErrorEvent.AddListener(OnFailedScrollInput); + ActiveScroll.OnMMTServerComunicationError.AddListener(OnShowErrorMessage); + ActiveScroll.OnScrollDynamicInfoError.AddListener(OnFailedScrollInput); + ActiveScroll.OnScrollApplicationError.AddListener(OnFailedScrollInput); + ActiveScroll.OnCancelErrorState.AddListener(HidePopUp); + } + void OnDisable() + { + ActiveScroll.OnMMTServerComunicationError.RemoveListener(OnShowErrorMessage); + ActiveScroll.OnScrollDynamicInfoError.RemoveListener(OnFailedScrollInput); + ActiveScroll.OnScrollApplicationError.RemoveListener(OnFailedScrollInput); + ActiveScroll.OnCancelErrorState.RemoveListener(HidePopUp); + } + + void Awake() + { CloseButton.onClick.RemoveAllListeners(); CloseButton.onClick.AddListener(HidePopUp); @@ -60,6 +75,15 @@ public void OnFailedScrollInput(ScrollApplicationCheckingError[] errorInfo) MessageText = generateHelpfulMessageAndAnimateScrollParam(errorInfo); ShowTimedPopUp(); } + /// <summary> + /// this method gets an error message as parameter and shows it in a popup. + /// </summary> + /// <param name="errorMsg"></param> + public void OnShowErrorMessage(string errorMsg) + { + MessageText = errorMsg; + ShowTimedPopUp(); + } private string generateHelpfulMessageAndAnimateScrollParam(ScrollApplicationCheckingError[] errorInfo) { @@ -141,7 +165,7 @@ private MMTFact parseFactFromError(ScrollApplicationCheckingError error) factUri += "?" + factLabel; //find the required fact in the active scroll thats invalidly assigned - return ScrollDetails.ActiveScroll?.requiredFacts + return ActiveScroll.Instance.Scroll.requiredFacts .Find(decl => decl.@ref.uri == error.fact.uri); } } diff --git a/Assets/Scripts/UI/InGame/ScrollDetails.cs b/Assets/Scripts/UI/InGame/ScrollDetails.cs new file mode 100644 index 00000000..85f4e63f --- /dev/null +++ b/Assets/Scripts/UI/InGame/ScrollDetails.cs @@ -0,0 +1,111 @@ +using REST_JSON_API; +using System.Collections.Generic; +using TMPro; +using UnityEngine; +using static CommunicationEvents; + +public class ScrollDetails : ScrollView +{ + public static ScrollDetails Instance + { + get => _Instance; + set + { + if (_Instance != value) + Destroy(_Instance); + + _Instance = value; + } + } + private static ScrollDetails _Instance; + + public WorldCursor cursor; + public GameObject parameterDisplayPrefab; + public GameObject mmtAnswerPopUp; + private PopupBehavior Popup; + + public static List<RenderedScrollFact> ParameterDisplays { get; private set; } + + void Awake() + { + Instance = this; + + if (cursor == null) + cursor = FindObjectOfType<WorldCursor>(); + + Popup = mmtAnswerPopUp.GetComponent<PopupBehavior>(); + Popup.gameObject.SetActive(true); // force Awake + } + + private void OnEnable() + { + ActiveScroll.OnScrollChanged.AddListener(ShowScroll); + ActiveScroll.OnFactAssignmentUpdated.AddListener(OnFactAssignmentUpdated); + ActiveScroll.OnScrollDynamicInfoUpdated.AddListener(UpdateScrollDescription); + } + + private void OnDisable() + { + ActiveScroll.OnScrollChanged.RemoveListener(ShowScroll); + ActiveScroll.OnFactAssignmentUpdated.RemoveListener(OnFactAssignmentUpdated); + ActiveScroll.OnScrollDynamicInfoUpdated.RemoveListener(UpdateScrollDescription); + } + + private void OnDestroy() + { + _Instance = null; + } + + /// <summary> + /// Update the scroll details screen to display the currently active scroll + /// </summary> + /// <param name="activeScroll"></param> + private void ShowScroll(ActiveScroll activeScroll) + { + Transform originalScroll = gameObject.transform.GetChild(1); + Transform originalScrollView = originalScroll.GetChild(1); + Transform originalViewport = originalScrollView.GetChild(0); + + //Clear all current ScrollFacts + originalViewport.GetChild(0).gameObject.DestroyAllChildren(); + Scroll scroll = activeScroll.Scroll; + + originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = scroll.description; + + //ParameterDisplays.ForEach(gameObj => Destroy(gameObj)); + ParameterDisplays = new(); + // generate parameter display slots + foreach (var slot in activeScroll.Assignments) + { + GameObject originalObj = + Instantiate(parameterDisplayPrefab, originalViewport.GetChild(0)); + + RenderedScrollFact originalRSF = + originalObj.GetComponentInChildren<RenderedScrollFact>(); + + ParameterDisplays.Add(originalRSF); + + originalRSF.Populate(scroll, slot.Key); + originalRSF.transform.parent.gameObject.SetActive(slot.Value.IsVisible); + } + } + + private void UpdateScrollDescription(Scroll rendered) + { + if (ActiveScroll.Instance.DynamicScrollDescriptionsActive) + { // Update scroll-description + Transform scroll = gameObject.transform.GetChild(1).transform; + scroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = rendered.description; + } + } + + private void OnFactAssignmentUpdated(string slotUri, ActiveScroll.SlotAssignment slotAssignment) + { + RenderedScrollFact changed = ParameterDisplays.Find(RSF => RSF.ScrollFactURI == slotUri); + + if (changed != null) + { + changed.Fact = slotAssignment.fact; + } + } +} diff --git a/Assets/Scripts/InventoryStuff/ScrollDetails.cs.meta b/Assets/Scripts/UI/InGame/ScrollDetails.cs.meta similarity index 100% rename from Assets/Scripts/InventoryStuff/ScrollDetails.cs.meta rename to Assets/Scripts/UI/InGame/ScrollDetails.cs.meta diff --git a/Assets/Scripts/UI/InGame/ScrollView.cs b/Assets/Scripts/UI/InGame/ScrollView.cs new file mode 100644 index 00000000..e3e95c89 --- /dev/null +++ b/Assets/Scripts/UI/InGame/ScrollView.cs @@ -0,0 +1,5 @@ +using UnityEngine; + +public abstract class ScrollView : MonoBehaviour +{ +} diff --git a/Assets/Scripts/UI/InGame/ScrollView.cs.meta b/Assets/Scripts/UI/InGame/ScrollView.cs.meta new file mode 100644 index 00000000..29da970a --- /dev/null +++ b/Assets/Scripts/UI/InGame/ScrollView.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01651b93334ae6f46a3b05f7cfe34543 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/UI/InGame/WebViewController.cs b/Assets/Scripts/UI/InGame/WebViewController.cs index e97f8488..d4c24bb0 100644 --- a/Assets/Scripts/UI/InGame/WebViewController.cs +++ b/Assets/Scripts/UI/InGame/WebViewController.cs @@ -12,7 +12,7 @@ using UnityEngine; using UnityEngine.Events; -public class WebViewController : MonoBehaviour +public class WebViewController : ScrollView { -- GitLab