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