namespace VRTK.Prefabs.Locomotion.DestinationLocations { using UnityEngine; using UnityEngine.Events; using System; using System.Collections.Generic; using Malimbe.MemberClearanceMethod; using Malimbe.XmlDocumentationAttribute; using Malimbe.PropertySerializationAttribute; using Malimbe.BehaviourStateRequirementMethod; using Zinnia.Rule; using Zinnia.Data.Type; using Zinnia.Extension; /// /// Represents an intersectable volume that can defer its properties to another location. /// public class DestinationLocation : MonoBehaviour { /// /// Defines the event with the specified . /// [Serializable] public class SurfaceDataUnityEvent : UnityEvent { } /// /// Defines the event with the specified . /// [Serializable] public class TransformDataUnityEvent : UnityEvent { } #region Location Settings /// /// The destination to defer the selected action output to. /// [Serialized, Cleared] [field: Header("Location Settings"), DocumentedByXml] public GameObject Destination { get; set; } /// /// The overriding origin of where to lock any destination points to. /// [Serialized, Cleared] [field: DocumentedByXml] public GameObject Origin { get; set; } /// /// Apply the rotation of the to the selected action output. /// [Serialized] [field: DocumentedByXml] public bool ApplyDestinationRotation { get; set; } = true; /// /// Emits the event for all when is executed. /// [Serialized] [field: DocumentedByXml] public bool EmitExitOnSelect { get; set; } = true; /// /// Allows to optionally determine which sources can affect the location. /// [Serialized, Cleared] [field: DocumentedByXml] public RuleContainer SourceValidity { get; set; } #endregion #region Location Events /// /// Emitted when the Destination Location is entered for the first time. /// [Header("Location Events"), DocumentedByXml] public UnityEvent HoverActivated = new UnityEvent(); /// /// Emitted when the Destination Location is entered. /// [DocumentedByXml] public SurfaceDataUnityEvent Entered = new SurfaceDataUnityEvent(); /// /// Emitted when the Destination Location is exited. /// [DocumentedByXml] public SurfaceDataUnityEvent Exited = new SurfaceDataUnityEvent(); /// /// Emitted when the Destination Location is exited for the last time. /// [DocumentedByXml] public UnityEvent HoverDeactivated = new UnityEvent(); /// /// Emitted when the Destination Location is activated. /// [DocumentedByXml] public TransformDataUnityEvent Activated = new TransformDataUnityEvent(); /// /// Emitted when the Destination Location is deactivated. /// [DocumentedByXml] public UnityEvent Deactivated = new UnityEvent(); #endregion /// /// Whether the Destination Location is currently being hovered over. /// public bool IsHovered { get; protected set; } /// /// Whether the Destination Location is currently activated. /// public bool IsActivated { get; protected set; } /// /// A collection of elements that are currently hovering over the Destination Location. /// public List HoveringElements { get; protected set; } = new List(); /// /// The payload to emit when the Destination Location is selected. /// protected TransformData selectedPayload = new TransformData(); /// /// A temporary container to store and mutate data from the hovering . /// protected RaycastHit hoverHit = new RaycastHit(); /// /// Handles a new enter action. /// /// The data that is entering the location. [RequiresBehaviourState] public virtual void Enter(SurfaceData data) { if (data.Transform == null || !SourceValidity.Accepts(data.Transform)) { return; } IsHovered = true; HoveringElements.Add(data); if (HoveringElements.Count == 1) { HoverActivated?.Invoke(); } Entered?.Invoke(CreateHoverPayload(data)); } /// /// Handles a new exit action. /// /// The data that is exiting the location. [RequiresBehaviourState] public virtual void Exit(SurfaceData data) { if (data.Transform == null || !SourceValidity.Accepts(data.Transform) || !HoveringElements.Contains(data)) { return; } IsHovered = false; HoveringElements.Remove(data); Exited?.Invoke(CreateHoverPayload(data)); if (HoveringElements.Count == 0) { HoverDeactivated?.Invoke(); } } /// /// Handles a new select action. /// /// The data that is selecting the location. [RequiresBehaviourState] public virtual void Select(SurfaceData data) { if (data.Transform == null || !SourceValidity.Accepts(data.Transform) || !HoveringElements.Contains(data)) { return; } IsActivated = true; Activated?.Invoke(CreateSelectedPayload(data)); if (!EmitExitOnSelect) { return; } foreach (SurfaceData element in HoveringElements.ToArray()) { Exit(element); } HoveringElements.Clear(); } /// /// Handles a new deselect action. /// [RequiresBehaviourState] public virtual void Deselect() { IsActivated = false; Deactivated?.Invoke(); } /// /// Creates the payload to emit on the hovering events of and . /// /// The data that is mutating the hover state. /// The data to emit. protected virtual SurfaceData CreateHoverPayload(SurfaceData data) { if (Origin == null || !Origin.activeInHierarchy) { return data; } hoverHit = data.CollisionData; hoverHit.point = Origin.transform.position; data.CollisionData = hoverHit; return data; } /// /// Creates the payload to emit on the event. /// /// The default data to potentially mutate. /// The data to emit. protected virtual TransformData CreateSelectedPayload(SurfaceData data) { if (Destination == null || !Destination.activeInHierarchy) { return data; } selectedPayload.Clear(); selectedPayload.Transform = Destination.transform; if (!ApplyDestinationRotation) { selectedPayload.RotationOverride = data.Rotation; } return selectedPayload; } } }