using System; using System.Collections; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; namespace Autohand { [Serializable, DefaultExecutionOrder(100)] public class UnityDispenserEvent : UnityEvent { } public class DispenserPoint : MonoBehaviour { [AutoHeader("Dispenser Point")] public bool ignoreMe; [AutoSmallHeader("Dispenser Settings")] public bool showeSettings = true; [Tooltip("The object to be copied and dispensed")] public Grabbable dispenseObject; [Tooltip("The maximum copies allowed to exist from this dispenser before they are destroyed or reset")] public int maxCopies = 3; [Tooltip("The delay in seconds before the next dispense appears after the current dispense is taken")] public float resetDelay = 0f; [Tooltip("Whether or not objects placed in the dispense point should be set to kinematic on placed or not")] public bool disableBody = false; [NaughtyAttributes.HideIf("disableBody"), Tooltip("Whether or not objects placed in the dispense point should be set to kinematic on placed or not")] public bool isKinematic = true; [Tooltip("If true the object will not just reset its position on reset it will be destroyed and a new copy will be placed. Less performant but important for things like ammo that should always respawn as new clips full")] public bool destroyOnReset = false; [Tooltip("The maximum distance a dispensed object can move from the point before the next object is dispensed")] public float maxDistance = 1f; [Space] public UnityDispenserEvent OnGrabDispenseEvent; public UnityDispenserEvent OnDispenseEvent; Grabbable currentDispense; Grabbable lastDispense; GameObject[] dispensePool; int poolCount; Coroutine dispenseRoutine; protected virtual void Start() { GameObject instanceObject; dispenseObject.body.gameObject.SetActive(false); instanceObject = Instantiate(dispenseObject.body.gameObject); instanceObject.transform.position = transform.position; instanceObject.transform.rotation = transform.rotation; instanceObject.SetActive(true); dispensePool = new GameObject[maxCopies]; dispensePool[0] = instanceObject; if(dispensePool[0].HasGrabbable(out var grab)) { if(!disableBody && isKinematic && grab.body != null) grab.body.isKinematic = true; grab.OnGrabEvent += OnGrab; grab.OnPlacePointAddEvent += OnPlaced; currentDispense = grab; if(disableBody) grab.DeactivateRigidbody(); } poolCount++; } protected virtual void OnDisable() { if(dispenseRoutine != null) StopCoroutine(dispenseRoutine); dispenseRoutine = null; } protected virtual void FixedUpdate() { if(maxDistance > 0 && currentDispense.gameObject.activeInHierarchy && Vector3.Distance(transform.position, currentDispense.rootTransform.position) > maxDistance) Dispense(); } public virtual Grabbable Dispense() { if(dispenseRoutine == null) { var poolIndex = (poolCount) % maxCopies; if(destroyOnReset) { Destroy(dispensePool[poolIndex]); dispensePool[poolIndex] = null; } if(poolCount < maxCopies || dispensePool[poolIndex] == null || dispensePool[poolIndex].activeInHierarchy == false) dispensePool[poolIndex] = Instantiate(dispenseObject.body.gameObject); dispensePool[poolIndex].transform.position = transform.position; dispensePool[poolIndex].transform.rotation = transform.rotation; if(dispensePool[poolIndex].HasGrabbable(out var grab)) { grab.ForceHandsRelease(); if(grab.placePoint != null) grab.placePoint.Remove(); if(grab.body == null) grab.ActivateRigidbody(); if(!disableBody && isKinematic) grab.body.isKinematic = true; if(!grab.body.isKinematic) { grab.body.velocity = Vector3.zero; grab.body.angularVelocity = Vector3.zero; } grab.OnGrabEvent += OnGrab; grab.OnPlacePointAddEvent += OnPlaced; dispenseRoutine = StartCoroutine(DispenseResetDelay(grab)); lastDispense = currentDispense; lastDispense.OnGrabEvent -= OnGrab; lastDispense.OnPlacePointAddEvent -= OnPlaced; currentDispense = grab; poolCount++; return grab; } poolCount++; } return null; } public virtual void OnGrab(Hand hand, Grabbable grab) { if(grab != null && isKinematic && grab.body != null) grab.body.isKinematic = false; OnGrabDispenseEvent?.Invoke(this, grab); Dispense(); } public virtual void OnPlaced(PlacePoint point, Grabbable grab) { if(grab != null && isKinematic && grab.body != null) grab.body.isKinematic = false; Debug.Log("Placed: " + grab.body.isKinematic); Dispense(); } IEnumerator DispenseResetDelay(Grabbable dispenseObject) { dispenseObject.body.gameObject.SetActive(false); yield return new WaitForSeconds(resetDelay); dispenseObject.body.gameObject.SetActive(true); dispenseObject.IgnoreGrabbableCollisionUntilNone(lastDispense); foreach(var hand in lastDispense.GetHeldBy()) dispenseObject.IgnoreHandCollisionUntilNone(hand); OnDispenseEvent?.Invoke(this, dispenseObject); if(disableBody) dispenseObject.DeactivateRigidbody(); dispenseRoutine = null; } } }