namespace VRTK.Prefabs.Interactions.Interactables.Grab
{
using UnityEngine;
using System.Collections.Generic;
using Malimbe.MemberChangeMethod;
using Malimbe.MemberClearanceMethod;
using Malimbe.XmlDocumentationAttribute;
using Malimbe.PropertySerializationAttribute;
using Zinnia.Extension;
using Zinnia.Data.Attribute;
using VRTK.Prefabs.Interactions.Interactors;
using VRTK.Prefabs.Interactions.Interactables.Grab.Action;
using VRTK.Prefabs.Interactions.Interactables.Grab.Receiver;
using VRTK.Prefabs.Interactions.Interactables.Grab.Provider;
///
/// Sets up the Interactable Prefab grab settings based on the provided user settings.
///
public class GrabInteractableConfigurator : MonoBehaviour
{
#region Facade Settings
///
/// The public interface facade.
///
[Serialized]
[field: Header("Facade Settings"), DocumentedByXml, Restricted]
public InteractableFacade Facade { get; protected set; }
#endregion
#region Action Settings
///
/// The action to perform when grabbing the interactable for the first time.
///
[Serialized, Cleared]
[field: Header("Action Settings"), DocumentedByXml]
public GrabInteractableAction PrimaryAction { get; set; }
///
/// The action to perform when grabbing the interactable for the second time.
///
[Serialized, Cleared]
[field: DocumentedByXml]
public GrabInteractableAction SecondaryAction { get; set; }
#endregion
#region Reference Settings
///
/// The Grab Receiver setup.
///
[Serialized]
[field: Header("Reference Settings"), DocumentedByXml, Restricted]
public GrabInteractableReceiver GrabReceiver { get; protected set; }
///
/// The Grab Provider setup.
///
[Serialized]
[field: Header("Reference Settings"), DocumentedByXml, Restricted]
public GrabInteractableInteractorProvider GrabProvider { get; protected set; }
#endregion
///
/// A collection of Interactors that are currently grabbing the Interactable.
///
public IReadOnlyList GrabbingInteractors => GrabProvider.GrabbingInteractors;
///
/// Determines if the grab type is set to toggle.
///
public bool IsGrabTypeToggle => GrabReceiver.GrabType == GrabInteractableReceiver.ActiveType.Toggle;
///
/// Attempt to grab the Interactable to the given Interactor.
///
/// The Interactor to attach the Interactable to.
public virtual void Grab(InteractorFacade interactor)
{
interactor.Grab(Facade);
}
///
/// Attempt to ungrab the Interactable.
///
/// The Interactor sequence index to ungrab from.
public virtual void Ungrab(int sequenceIndex = 0)
{
if (GrabbingInteractors == null || GrabbingInteractors.Count == 0 || sequenceIndex >= GrabbingInteractors.Count)
{
return;
}
Ungrab(GrabbingInteractors[sequenceIndex]);
}
///
/// Attempts to ungrab the Interactable.
///
/// The Interactor to ungrab from.
public virtual void Ungrab(InteractorFacade interactor)
{
interactor.Ungrab();
}
///
/// Notifies that the Interactable is being grabbed.
///
/// The grabbing object.
public virtual void NotifyGrab(GameObject data)
{
InteractorFacade interactor = data.TryGetComponent(true, true);
if (interactor != null)
{
if (Facade.GrabbingInteractors.Count == 1)
{
Facade.FirstGrabbed?.Invoke(interactor);
}
Facade.Grabbed?.Invoke(interactor);
interactor.NotifyOfGrab(Facade);
}
}
///
/// Notifies that the Interactable is no longer being grabbed.
///
/// The previous grabbing object.
public virtual void NotifyUngrab(GameObject data)
{
InteractorFacade interactor = data.TryGetComponent(true, true);
if (interactor != null)
{
Facade.Ungrabbed?.Invoke(interactor);
interactor.NotifyOfUngrab(Facade);
if (Facade.GrabbingInteractors.Count == 0)
{
Facade.LastUngrabbed?.Invoke(interactor);
}
}
}
///
/// Sets the consumer containers to the current active container.
///
public virtual void ConfigureContainer()
{
GrabReceiver.ConfigureConsumerContainers(Facade.ConsumerContainer);
ConfigureActionContainer(PrimaryAction);
ConfigureActionContainer(SecondaryAction);
}
protected virtual void OnEnable()
{
LinkReceiverToProvider();
LinkToPrimaryAction();
LinkToSecondaryAction();
ConfigureContainer();
GrabReceiver.GrabValidity.ReceiveValidity = Facade.DisallowedGrabInteractors;
}
protected virtual void OnDisable()
{
UnlinkReceiverToProvider();
UnlinkToPrimaryAction();
UnlinkToSecondaryAction();
}
///
/// Configures the action containers.
///
/// The action to configure.
protected virtual void ConfigureActionContainer(GrabInteractableAction action)
{
action.RunWhenActiveAndEnabled(() => action.GrabSetup = this);
}
///
/// Links the Grab Receiver to the Grab Provider.
///
protected virtual void LinkReceiverToProvider()
{
GrabReceiver.OutputGrabAction.Emitted.AddListener(GrabProvider.InputGrabReceived.Receive);
GrabReceiver.OutputUngrabAction.Emitted.AddListener(GrabProvider.InputUngrabReceived.Receive);
GrabReceiver.OutputUngrabOnUntouchAction.Emitted.AddListener(Facade.Ungrab);
}
///
/// Unlinks the Grab Receiver to the Grab Provider.
///
protected virtual void UnlinkReceiverToProvider()
{
GrabReceiver.OutputGrabAction.Emitted.RemoveListener(GrabProvider.InputGrabReceived.Receive);
GrabReceiver.OutputUngrabAction.Emitted.RemoveListener(GrabProvider.InputUngrabReceived.Receive);
GrabReceiver.OutputUngrabOnUntouchAction.Emitted.RemoveListener(Facade.Ungrab);
}
///
/// Links the Grab Receiver and Grab Provider to the Primary Grab Action.
///
protected virtual void LinkToPrimaryAction()
{
if (PrimaryAction == null)
{
return;
}
GrabReceiver.OutputActiveCollisionConsumer.Emitted.AddListener(PrimaryAction.InputActiveCollisionConsumer.Receive);
GrabProvider.OutputPrimaryGrabAction.Emitted.AddListener(PrimaryAction.InputGrabReceived.Receive);
GrabProvider.OutputPrimaryUngrabAction.Emitted.AddListener(PrimaryAction.InputUngrabReceived.Receive);
}
///
/// Unlinks the Grab Receiver and Grab Provider to the Primary Grab Action.
///
protected virtual void UnlinkToPrimaryAction()
{
if (PrimaryAction == null)
{
return;
}
GrabReceiver.OutputActiveCollisionConsumer.Emitted.RemoveListener(PrimaryAction.InputActiveCollisionConsumer.Receive);
GrabProvider.OutputPrimaryGrabAction.Emitted.RemoveListener(PrimaryAction.InputGrabReceived.Receive);
GrabProvider.OutputPrimaryUngrabAction.Emitted.RemoveListener(PrimaryAction.InputUngrabReceived.Receive);
}
///
/// Links the Grab Receiver and Grab Provider to the Secondary Grab Action.
///
protected virtual void LinkToSecondaryAction()
{
if (SecondaryAction == null)
{
return;
}
GrabReceiver.OutputActiveCollisionConsumer.Emitted.AddListener(SecondaryAction.InputActiveCollisionConsumer.Receive);
GrabProvider.OutputPrimaryGrabSetupOnSecondaryAction.Emitted.AddListener(SecondaryAction.InputGrabSetup.Receive);
GrabProvider.OutputPrimaryUngrabResetOnSecondaryAction.Emitted.AddListener(SecondaryAction.InputUngrabReset.Receive);
GrabProvider.OutputSecondaryGrabAction.Emitted.AddListener(SecondaryAction.InputGrabReceived.Receive);
GrabProvider.OutputSecondaryUngrabAction.Emitted.AddListener(SecondaryAction.InputUngrabReceived.Receive);
}
///
/// Unlinks the Grab Receiver and Grab Provider to the Secondary Grab Action.
///
protected virtual void UnlinkToSecondaryAction()
{
if (SecondaryAction == null)
{
return;
}
GrabReceiver.OutputActiveCollisionConsumer.Emitted.RemoveListener(SecondaryAction.InputActiveCollisionConsumer.Receive);
GrabProvider.OutputPrimaryGrabSetupOnSecondaryAction.Emitted.RemoveListener(SecondaryAction.InputGrabSetup.Receive);
GrabProvider.OutputPrimaryUngrabResetOnSecondaryAction.Emitted.RemoveListener(SecondaryAction.InputUngrabReset.Receive);
GrabProvider.OutputSecondaryGrabAction.Emitted.RemoveListener(SecondaryAction.InputGrabReceived.Receive);
GrabProvider.OutputSecondaryUngrabAction.Emitted.RemoveListener(SecondaryAction.InputUngrabReceived.Receive);
}
///
/// Called after has been changed.
///
[CalledBeforeChangeOf(nameof(PrimaryAction))]
protected virtual void OnBeforePrimaryActionChange()
{
UnlinkToPrimaryAction();
}
///
/// Called after has been changed.
///
[CalledAfterChangeOf(nameof(PrimaryAction))]
protected virtual void OnAfterPrimaryActionChange()
{
LinkToPrimaryAction();
ConfigureActionContainer(PrimaryAction);
}
///
/// Called after has been changed.
///
[CalledBeforeChangeOf(nameof(SecondaryAction))]
protected virtual void OnBeforeSecondaryActionChange()
{
UnlinkToSecondaryAction();
}
///
/// Called after has been changed.
///
[CalledAfterChangeOf(nameof(SecondaryAction))]
protected virtual void OnAfterSecondaryActionChange()
{
LinkToSecondaryAction();
ConfigureActionContainer(SecondaryAction);
}
}
}