namespace Tilia.Interactions.Interactables.Interactables.Touch
{
using System.Collections.Generic;
using Tilia.Interactions.Interactables.Interactors;
using UnityEngine;
using Zinnia.Data.Attribute;
using Zinnia.Data.Collection.Counter;
using Zinnia.Data.Collection.List;
using Zinnia.Event.Proxy;
using Zinnia.Extension;
using Zinnia.Tracking.Collision;
using Zinnia.Tracking.Collision.Active;
using Zinnia.Tracking.Collision.Active.Operation.Extraction;
public class TouchInteractableConfigurator : MonoBehaviour
{
#region Facade Settings
[Header("Facade Settings")]
[Tooltip("The public interface facade.")]
[SerializeField]
[Restricted]
private InteractableFacade facade;
///
/// The public interface facade.
///
public InteractableFacade Facade
{
get
{
return facade;
}
set
{
facade = value;
}
}
#endregion
#region Touch Consumer Settings
[Header("Touch Consumer Settings")]
[Tooltip("The ActiveCollisionConsumer that listens for the touch payload.")]
[SerializeField]
[Restricted]
private ActiveCollisionConsumer touchConsumer;
///
/// The that listens for the touch payload.
///
public ActiveCollisionConsumer TouchConsumer
{
get
{
return touchConsumer;
}
set
{
touchConsumer = value;
}
}
[Tooltip("The ActiveCollisionConsumer that listens for the untouch payload.")]
[SerializeField]
[Restricted]
private ActiveCollisionConsumer untouchConsumer;
///
/// The that listens for the untouch payload.
///
public ActiveCollisionConsumer UntouchConsumer
{
get
{
return untouchConsumer;
}
set
{
untouchConsumer = value;
}
}
#endregion
#region Touch Settings
[Header("Touch Settings")]
[Tooltip("The GameObjectObservableList that holds the current touching objects data.")]
[SerializeField]
[Restricted]
private GameObjectObservableList currentTouchingObjects;
///
/// The that holds the current touching objects data.
///
public GameObjectObservableList CurrentTouchingObjects
{
get
{
return currentTouchingObjects;
}
set
{
currentTouchingObjects = value;
}
}
[Tooltip("The GameObjectObservableList that holds the current untouching objects data.")]
[SerializeField]
[Restricted]
private GameObjectObservableList currentUntouchingObjects;
///
/// The that holds the current untouching objects data.
///
public GameObjectObservableList CurrentUntouchingObjects
{
get
{
return currentUntouchingObjects;
}
set
{
currentUntouchingObjects = value;
}
}
[Tooltip("The GameObjectEventProxyEmitter used to determine the untouch actions.")]
[SerializeField]
[Restricted]
private GameObjectEventProxyEmitter currentUntouchingEventProxy;
///
/// The used to determine the untouch actions.
///
public GameObjectEventProxyEmitter CurrentUntouchingEventProxy
{
get
{
return currentUntouchingEventProxy;
}
set
{
currentUntouchingEventProxy = value;
}
}
[Tooltip("The GameObjectEventProxyEmitter used to determine the touch validity.")]
[SerializeField]
[Restricted]
private GameObjectEventProxyEmitter touchValidity;
///
/// The used to determine the touch validity.
///
public GameObjectEventProxyEmitter TouchValidity
{
get
{
return touchValidity;
}
set
{
touchValidity = value;
}
}
#endregion
#region Interactor Settings
[Header("Interactor Settings")]
[Tooltip("The ActiveCollisionsContainer for potential interactors.")]
[SerializeField]
[Restricted]
private ActiveCollisionsContainer potentialInteractors;
///
/// The for potential interactors.
///
public ActiveCollisionsContainer PotentialInteractors
{
get
{
return potentialInteractors;
}
set
{
potentialInteractors = value;
}
}
[Tooltip("The GameObjectObservableCounter for counting active interactors.")]
[SerializeField]
[Restricted]
private GameObjectObservableCounter activeInteractorCounter;
///
/// The for counting active interactors.
///
public GameObjectObservableCounter ActiveInteractorCounter
{
get
{
return activeInteractorCounter;
}
set
{
activeInteractorCounter = value;
}
}
[Tooltip("The NotifierContainerExtractor for adding active interactors.")]
[SerializeField]
[Restricted]
private NotifierContainerExtractor addActiveInteractor;
///
/// The for adding active interactors.
///
public NotifierContainerExtractor AddActiveInteractor
{
get
{
return addActiveInteractor;
}
set
{
addActiveInteractor = value;
}
}
[Tooltip("The NotifierContainerExtractor for removing active interactors.")]
[SerializeField]
[Restricted]
private NotifierContainerExtractor removeActiveInteractor;
///
/// The for removing active interactors.
///
public NotifierContainerExtractor RemoveActiveInteractor
{
get
{
return removeActiveInteractor;
}
set
{
removeActiveInteractor = value;
}
}
#endregion
///
/// A collection of Interactors that are currently touching the Interactable.
///
public virtual IReadOnlyList TouchingInteractors => GetTouchingInteractors();
///
/// A reusable collection to hold the returned touching interactors.
///
protected readonly List touchingInteractors = new List();
///
/// Whether this is being first touched.
///
protected bool isFirstTouched;
///
/// Enforces that all the existing touching interactors are no longer actually touching.
///
public virtual void UntouchAllTouchingInteractors()
{
for (int index = TouchingInteractors.Count - 1; index >= 0; index--)
{
CurrentUntouchingEventProxy.Receive(TouchingInteractors[index].gameObject);
}
}
///
/// Notifies that the Interactable is being touched.
///
/// The touching object.
public virtual void NotifyTouch(GameObject data)
{
InteractorFacade interactor = data.TryGetComponent(true, true);
if (interactor != null)
{
if (TouchingInteractors.Count <= 1 && !isFirstTouched)
{
isFirstTouched = true;
Facade.FirstTouched?.Invoke(interactor);
}
Facade.Touched?.Invoke(interactor);
interactor.NotifyOfTouch(Facade);
}
}
///
/// Notifies that the Interactable is being no longer touched.
///
/// The previous touching object.
public virtual void NotifyUntouch(GameObject data)
{
InteractorFacade interactor = data.TryGetComponent(true, true);
if (interactor != null)
{
Facade.Untouched?.Invoke(interactor);
interactor.NotifyOfUntouch(Facade);
if (TouchingInteractors.Count == 0)
{
isFirstTouched = false;
Facade.LastUntouched?.Invoke(interactor);
}
}
}
///
/// Sets the consumer containers to the current active container.
///
public virtual void ConfigureContainer()
{
TouchConsumer.Container = Facade.Configuration.ConsumerContainer;
UntouchConsumer.Container = Facade.Configuration.ConsumerContainer;
}
///
/// Retrieves a collection of Interactors that are touching the Interactable.
///
/// The touching Interactors.
protected virtual IReadOnlyList GetTouchingInteractors()
{
touchingInteractors.Clear();
if (CurrentTouchingObjects == null)
{
return touchingInteractors;
}
foreach (GameObject element in CurrentTouchingObjects.NonSubscribableElements)
{
InteractorFacade interactor = element.TryGetComponent(true, true);
if (interactor != null)
{
touchingInteractors.Add(interactor);
}
}
return touchingInteractors;
}
protected virtual void OnEnable()
{
ConfigureContainer();
TouchValidity.ReceiveValidity = Facade.Configuration.DisallowedTouchInteractors;
LinkActiveInteractorCollisions();
}
protected virtual void OnDisable()
{
UnlinkActiveInteractorCollisions();
}
///
/// Links the to the potential and active interactor logic.
///
protected virtual void LinkActiveInteractorCollisions()
{
Facade.Configuration.CollisionNotifier.CollisionStarted.AddListener(PotentialInteractors.Add);
Facade.Configuration.CollisionNotifier.CollisionStarted.AddListener(AddActiveInteractor.DoExtract);
Facade.Configuration.CollisionNotifier.CollisionChanged.AddListener(ProcessPotentialInteractorContentChange);
Facade.Configuration.CollisionNotifier.CollisionStopped.AddListener(PotentialInteractors.Remove);
Facade.Configuration.CollisionNotifier.CollisionStopped.AddListener(RemoveActiveInteractor.DoExtract);
}
///
/// Unlinks the to the potential and active interactor logic.
///
protected virtual void UnlinkActiveInteractorCollisions()
{
Facade.Configuration.CollisionNotifier.CollisionStarted.RemoveListener(PotentialInteractors.Add);
Facade.Configuration.CollisionNotifier.CollisionStarted.RemoveListener(AddActiveInteractor.DoExtract);
Facade.Configuration.CollisionNotifier.CollisionChanged.RemoveListener(ProcessPotentialInteractorContentChange);
Facade.Configuration.CollisionNotifier.CollisionStopped.RemoveListener(PotentialInteractors.Remove);
Facade.Configuration.CollisionNotifier.CollisionStopped.RemoveListener(RemoveActiveInteractor.DoExtract);
}
///
/// Handles any change of collision contents.
///
/// The changed collision data.
protected virtual void ProcessPotentialInteractorContentChange(CollisionNotifier.EventData data)
{
PotentialInteractors.ProcessContentsChanged();
}
}
}