namespace Tilia.Interactions.Interactables.Interactables
{
using System;
using Tilia.Interactions.Interactables.Interactors;
using UnityEngine;
using Zinnia.Action;
using Zinnia.Action.Collection;
using Zinnia.Data.Attribute;
using Zinnia.Extension;
using Zinnia.Rule;
using Zinnia.Tracking.Collision.Active;
using Action = Zinnia.Action.Action;
///
/// Sets up the Interactor Action Receiver Prefab action settings based on the provided user settings.
///
public class InteractableActionReceiverConfigurator : MonoBehaviour
{
#region Facade Settings
[Header("Facade Settings")]
[Tooltip("The public interface facade.")]
[SerializeField]
[Restricted]
private InteractableActionReceiverFacade facade;
///
/// The public interface facade.
///
public InteractableActionReceiverFacade Facade
{
get
{
return facade;
}
set
{
facade = value;
}
}
#endregion
#region Reference Settings
[Header("Reference Settings")]
[Tooltip("The ActionObservableList that containts the Action collection that can be linked to the InteractorActionFacade.SourceAction.")]
[SerializeField]
[Restricted]
private ActionObservableList targetActions;
///
/// The that containts the collection that can be linked to the .
///
public ActionObservableList TargetActions
{
get
{
return targetActions;
}
set
{
targetActions = value;
}
}
[Tooltip("The ActionRegistrar to create the appropriate action links.")]
[SerializeField]
[Restricted]
private ActionRegistrar actionRegistrar;
///
/// The to create the appropriate action links.
///
public ActionRegistrar ActionRegistrar
{
get
{
return actionRegistrar;
}
set
{
actionRegistrar = value;
}
}
[Tooltip("The ActiveCollisionConsumer for checking valid start action.")]
[SerializeField]
[Restricted]
private ActiveCollisionConsumer startActionConsumer;
///
/// The for checking valid start action.
///
public ActiveCollisionConsumer StartActionConsumer
{
get
{
return startActionConsumer;
}
set
{
startActionConsumer = value;
}
}
[Tooltip("The ListContainsRule for the start action.")]
[SerializeField]
[Restricted]
private ListContainsRule receiveStartActionRule;
///
/// The for the start action.
///
public ListContainsRule ReceiveStartActionRule
{
get
{
return receiveStartActionRule;
}
set
{
receiveStartActionRule = value;
}
}
[Tooltip("The ActiveCollisionConsumer for checking valid stop action.")]
[SerializeField]
[Restricted]
private ActiveCollisionConsumer stopActionConsumer;
///
/// The for checking valid stop action.
///
public ActiveCollisionConsumer StopActionConsumer
{
get
{
return stopActionConsumer;
}
set
{
stopActionConsumer = value;
}
}
[Tooltip("The ListContainsRule for the stop action.")]
[SerializeField]
[Restricted]
private ListContainsRule receiveStopActionRule;
///
/// The for the stop action.
///
public ListContainsRule ReceiveStopActionRule
{
get
{
return receiveStopActionRule;
}
set
{
receiveStopActionRule = value;
}
}
#endregion
///
/// The parent to child the by default.
///
protected Transform defaultParent;
///
/// Links the to the payload source containers on the start and stop publishers.
///
public virtual void LinkInteractableToConsumers()
{
Facade.transform.SetParent(defaultParent);
if (Facade == null || Facade.TargetInteractable == null)
{
return;
}
Facade.transform.SetParent(Facade.TargetInteractable.transform);
StartActionConsumer.Container = Facade.TargetInteractable.gameObject;
StopActionConsumer.Container = Facade.TargetInteractable.gameObject;
}
///
/// Registers the activation/deactivation events for the on the appropraite .
///
/// The interactable to register the events on.
/// The interaction state of the interactable to register the events on.
public virtual void RegisterInteractableEvents(InteractableFacade interactable, InteractableActionReceiverFacade.InteractionState interactionState)
{
if (interactable == null)
{
return;
}
switch (interactionState)
{
case InteractableActionReceiverFacade.InteractionState.FirstTouch:
interactable.Touched.AddListener(EnableFirstTouchedOnActionRegistrar);
interactable.Untouched.AddListener(DisableFirstTouchedOnActionRegistrar);
break;
case InteractableActionReceiverFacade.InteractionState.Touch:
interactable.Touched.AddListener(Facade.EnableActionRegistrar);
interactable.Untouched.AddListener(Facade.DisableActionRegistrar);
break;
case InteractableActionReceiverFacade.InteractionState.FirstGrab:
interactable.FirstGrabbed.AddListener(Facade.EnableActionRegistrar);
interactable.LastUngrabbed.AddListener(Facade.DisableActionRegistrar);
break;
case InteractableActionReceiverFacade.InteractionState.Grab:
interactable.Grabbed.AddListener(Facade.EnableActionRegistrar);
interactable.Ungrabbed.AddListener(Facade.DisableActionRegistrar);
break;
}
}
///
/// Unregisters the activation/deactivation events for the from the appropraite .
///
/// The interactable to unregister the events from.
/// The interaction state of the interactable to unregister the events from.
public virtual void UnregisterInteractableEvents(InteractableFacade interactable, InteractableActionReceiverFacade.InteractionState interactionState)
{
if (interactable == null)
{
return;
}
switch (interactionState)
{
case InteractableActionReceiverFacade.InteractionState.FirstTouch:
interactable.Touched.RemoveListener(EnableFirstTouchedOnActionRegistrar);
interactable.Untouched.RemoveListener(DisableFirstTouchedOnActionRegistrar);
break;
case InteractableActionReceiverFacade.InteractionState.Touch:
interactable.Touched.RemoveListener(Facade.EnableActionRegistrar);
interactable.Untouched.RemoveListener(Facade.DisableActionRegistrar);
break;
case InteractableActionReceiverFacade.InteractionState.FirstGrab:
interactable.FirstGrabbed.RemoveListener(Facade.EnableActionRegistrar);
interactable.LastUngrabbed.RemoveListener(Facade.DisableActionRegistrar);
break;
case InteractableActionReceiverFacade.InteractionState.Grab:
interactable.Grabbed.RemoveListener(Facade.EnableActionRegistrar);
interactable.Ungrabbed.RemoveListener(Facade.DisableActionRegistrar);
break;
}
}
///
/// Processes the publishers linked in the collection.
///
public virtual void ProcessPublisherList()
{
ClearPublisherSetup();
Type currentActionType = null;
foreach (InteractorActionPublisherFacade publisher in Facade.SourcePublishers.SubscribableElements)
{
publisher.Configuration.RunWhenActiveAndEnabled(() => ProcessPublisher(publisher, ref currentActionType));
}
}
///
/// Notifies the Activated event on the .
///
/// The source Interactor.
public virtual void NotifyActivated(GameObject source)
{
InteractorFacade interactor = source.TryGetComponent();
if (interactor == null)
{
return;
}
Facade.Activated?.Invoke(interactor);
}
///
/// Notifies the Deactivated event on the .
///
/// The source Interactor.
public virtual void NotifyDeactivated(GameObject source)
{
InteractorFacade interactor = source.TryGetComponent();
if (interactor == null)
{
return;
}
Facade.Deactivated?.Invoke(interactor);
}
protected virtual void Awake()
{
defaultParent = Facade.transform.parent;
}
protected virtual void OnEnable()
{
LinkInteractableToConsumers();
RegisterInteractableEvents(Facade.TargetInteractable, Facade.ActivationState);
}
protected virtual void OnDisable()
{
UnregisterInteractableEvents(Facade.TargetInteractable, Facade.ActivationState);
}
///
/// Enables the first touched that is touching the on the action registrar.
///
/// Not used
protected virtual void EnableFirstTouchedOnActionRegistrar(InteractorFacade _)
{
if (Facade.TargetInteractable.Configuration.TouchConfiguration.CurrentTouchingObjects.NonSubscribableElements.Count > 0)
{
Facade.EnableActionRegistrar(Facade.TargetInteractable.Configuration.TouchConfiguration.CurrentTouchingObjects.NonSubscribableElements[0]);
}
}
///
/// Disables the given on the action registrar.
///
/// The interactor to disable.
protected virtual void DisableFirstTouchedOnActionRegistrar(InteractorFacade interactor)
{
Facade.DisableActionRegistrar(interactor);
EnableFirstTouchedOnActionRegistrar(null);
}
///
/// Clears the publisher setup.
///
protected virtual void ClearPublisherSetup()
{
ActionRegistrar.Target = null;
ActionRegistrar.Sources.Clear();
ReceiveStartActionRule.RunWhenActiveAndEnabled(() => ReceiveStartActionRule.Objects.Clear());
ReceiveStopActionRule.RunWhenActiveAndEnabled(() => ReceiveStopActionRule.Objects.Clear());
}
///
/// Determines if the given publisher element is valid.
///
/// The cached value to check against.
/// The current value to check with.
/// The message to display in the exception if the element is not valid.
/// Whether the given publisher element is valid.
protected virtual bool IsValidPublisherElement(object cachedValue, object currentValue, string exceptionMessage)
{
if (cachedValue != null && !cachedValue.Equals(currentValue))
{
ClearPublisherSetup();
throw new ArgumentException(exceptionMessage);
}
return true;
}
///
/// Activates the correct output based on the linked from the publisher.
///
/// The type to activate.
protected virtual void ActivateOutputAction(Type actionType)
{
foreach (Action action in TargetActions.NonSubscribableElements)
{
action.gameObject.SetActive(false);
if (action.GetType().IsAssignableFrom(actionType))
{
action.gameObject.SetActive(true);
ActionRegistrar.Target = action;
}
}
}
///
/// Sets up the links from the publisher to the receiver.
///
/// The publisher to link to.
protected virtual void SetupPublisherLinks(InteractorActionPublisherFacade publisher)
{
ActionRegistrar.ActionSource actionSource = new ActionRegistrar.ActionSource()
{
Enabled = false,
Container = publisher.SourceInteractor.gameObject,
Action = publisher.ActiveAction
};
ActionRegistrar.Sources.Add(actionSource);
ReceiveStartActionRule.Objects.Add(publisher.Configuration.StartActionPublisher.gameObject);
ReceiveStopActionRule.Objects.Add(publisher.Configuration.StopActionPublisher.gameObject);
}
///
/// Processes the given publisher data.
///
/// The publisher to process.
/// The previous publisher's action type.
protected virtual void ProcessPublisher(InteractorActionPublisherFacade publisher, ref Type previousActionType)
{
Type publisherActionType = publisher.ActiveAction.GetType();
IsValidPublisherElement(previousActionType, publisherActionType, string.Format("All `SourcePublishers` Action types must be identical. Mismatch found between previous value of `{0}` and the publisher `{1}` which has a value of `{2}`.", previousActionType, publisher.name, publisherActionType));
ActivateOutputAction(publisherActionType);
SetupPublisherLinks(publisher);
previousActionType = publisherActionType;
}
}
}