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;
}
}
}