#if VRC_SDK_VRCSDK2 using System; using System.Collections.Generic; using UnityEngine; using VRCSDK2; namespace VRCPrefabs.CyanEmu { [AddComponentMenu("")] public class CyanEmuTriggerHelper : MonoBehaviour, ICyanEmuInteractable, ICyanEmuPickupable, ICyanEmuStationHandler { private static readonly int PLAYER_LAYER = 1 << 9; // Player Layer public VRC_Trigger Trigger { get; private set; } public bool HasGlobalOnEnable { get; private set; } public bool HasGlobalOnDisable { get; private set; } private List enterTriggers = new List(); private List exitTriggers = new List(); private List enterCollider = new List(); private List exitCollider = new List(); private List particleCollider = new List(); private List timerTriggers = new List(); private List interactTriggers = new List(); private List onKeyTriggers = new List(); private bool hasObjectSync = false; private bool hasAddedListeners = false; public static void InitializeTrigger(VRC.SDKBase.VRC_Trigger trigger) { CyanEmuTriggerHelper helper = trigger.gameObject.AddComponent(); helper.SetTrigger(trigger as VRC_Trigger); trigger.ExecuteTrigger = helper.ExecuteTrigger; } public void ExecuteTrigger(VRC_Trigger.TriggerEvent trigger) { if ( (!gameObject.activeInHierarchy || !Trigger.enabled) && trigger.TriggerType != VRC.SDKBase.VRC_Trigger.TriggerType.OnDisable ) { return; } CyanEmuTriggerExecutor.ExecuteTrigger(trigger); } private void SetTrigger(VRC_Trigger trigger) { if (trigger == null) { this.LogError("Trigger is null. Destroying helper."); DestroyImmediate(this); return; } Trigger = trigger; CyanEmuTriggerExecutor.AddTrigger(Trigger); hasObjectSync = GetComponent(); VRC_CombatSystem combatSystem = FindObjectOfType(); // Go through and make sure all null targets reference itself. for (int trig = 0; trig < Trigger.Triggers.Count; ++trig) { VRC_Trigger.TriggerEvent trigEvent = Trigger.Triggers[trig]; for (int trigEventInd = 0; trigEventInd < trigEvent.Events.Count; ++trigEventInd) { VRC_EventHandler.VrcEvent vrcEvent = trigEvent.Events[trigEventInd]; GameObject obj = gameObject; bool isCombat = false; if ( (vrcEvent.EventType == VRC_EventHandler.VrcEventType.AddDamage || vrcEvent.EventType == VRC_EventHandler.VrcEventType.AddHealth) && combatSystem != null ) { obj = combatSystem.gameObject; isCombat = true; } if (vrcEvent.ParameterObjects == null || vrcEvent.ParameterObjects.Length == 0) { if (vrcEvent.ParameterObject != null) { obj = vrcEvent.ParameterObject; } vrcEvent.ParameterObjects = new GameObject[] { obj }; this.LogWarning("VRC_Trigger[" + trig + "][" + trigEventInd + "] has no objects. Setting it to target itself. " + VRC.Tools.GetGameObjectPath(obj)); } else { bool found = false; for (int i = 0; i < vrcEvent.ParameterObjects.Length; ++i) { if (vrcEvent.ParameterObjects[i] == null) { vrcEvent.ParameterObjects[i] = obj; found = true; } } if (found && !isCombat) { this.LogWarning("VRC_Trigger[" + trig + "][" + trigEventInd + "] has null targets. Setting targets to itself. " + VRC.Tools.GetGameObjectPath(obj)); } } } } for (int i = 0; i < Trigger.Triggers.Count; ++i) { if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnEnterTrigger) { enterTriggers.Add(Trigger.Triggers[i]); CheckForPlayerLayerOverSync(Trigger.Triggers[i]); } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnExitTrigger) { exitTriggers.Add(Trigger.Triggers[i]); CheckForPlayerLayerOverSync(Trigger.Triggers[i]); } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnEnterCollider) { enterCollider.Add(Trigger.Triggers[i]); CheckForPlayerLayerOverSync(Trigger.Triggers[i]); } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnExitCollider) { exitCollider.Add(Trigger.Triggers[i]); CheckForPlayerLayerOverSync(Trigger.Triggers[i]); } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnTimer) { Trigger.ResetClock(Trigger.Triggers[i]); timerTriggers.Add(Trigger.Triggers[i]); if (Trigger.Triggers[i].BroadcastType.IsAlwaysBufferedBroadcastType()) { HasGlobalOnEnable = true; } } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnInteract) { interactTriggers.Add(Trigger.Triggers[i]); } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnKeyDown || Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnKeyUp) { onKeyTriggers.Add(Trigger.Triggers[i]); } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnParticleCollision) { particleCollider.Add(Trigger.Triggers[i]); } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnPlayerJoined) { if (Trigger.Triggers[i].BroadcastType.IsAlwaysBufferedBroadcastType()) { this.LogWarning("Oversync on player joined! " + Trigger.Triggers[i].GetTriggerEventAsString()); } } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnPlayerLeft) { if (Trigger.Triggers[i].BroadcastType.IsAlwaysBufferedBroadcastType()) { this.LogWarning("Oversync on player left! " + Trigger.Triggers[i].GetTriggerEventAsString()); } } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnEnable) { if (Trigger.Triggers[i].BroadcastType.IsAlwaysBufferedBroadcastType()) { HasGlobalOnEnable = true; } } else if (Trigger.Triggers[i].TriggerType == VRC_Trigger.TriggerType.OnDisable) { if (Trigger.Triggers[i].BroadcastType.IsAlwaysBufferedBroadcastType()) { HasGlobalOnDisable = true; } } } if (enterTriggers.Count + exitTriggers.Count + enterCollider.Count + exitCollider.Count + timerTriggers.Count + onKeyTriggers.Count + particleCollider.Count == 0) { enabled = false; } AddListeners(); } private void CheckForPlayerLayerOverSync(VRC_Trigger.TriggerEvent triggerEvent) { if ((triggerEvent.Layers & PLAYER_LAYER) != 0 && triggerEvent.BroadcastType.IsEveryoneBroadcastType()) { this.LogWarning("Player layer Enter/Exit trigger Oversync! " + triggerEvent.GetTriggerEventAsString()); } } private void OnTriggerEnter(Collider other) { HandleCollisionEvent(other.gameObject, enterTriggers); } private void OnTriggerExit(Collider other) { HandleCollisionEvent(other.gameObject, exitTriggers); } private void OnCollisionEnter(Collision collision) { HandleCollisionEvent(collision.gameObject, enterCollider); } private void OnCollisionExit(Collision collision) { HandleCollisionEvent(collision.gameObject, exitCollider); } private void OnParticleCollision(GameObject other) { HandleCollisionEvent(other, particleCollider); } private void HandleCollisionEvent(GameObject other, List events) { if (!Trigger.enabled) { return; } bool synced = other.GetComponent() != null || hasObjectSync; int layer = 1 << other.gameObject.layer; foreach (VRC_Trigger.TriggerEvent evt in events) { if ((evt.Layers & layer) != 0) { Trigger.ExecuteTrigger(evt); if (synced && evt.BroadcastType.IsEveryoneBroadcastType()) { this.LogWarning("Potential ObjectSync Enter/Exit trigger Oversync! " + evt.GetTriggerEventAsString()); } } } } // TODO optimize public void UpdateTimers(List eventsToFire) { if (!gameObject.activeInHierarchy || !Trigger.enabled) { return; } foreach (VRC_Trigger.TriggerEvent timer in timerTriggers) { if (timer.EventFired) { continue; } timer.Timer += Time.deltaTime; if (timer.Timer >= timer.Duration) { eventsToFire.Add(timer); timer.EventFired = true; if (timer.Repeat) { Trigger.ResetClock(timer); } } } } // TODO optimize public void UpdateOnKeyTriggers(List eventsToFire) { if (!gameObject.activeInHierarchy || !Trigger.enabled) { return; } foreach (VRC_Trigger.TriggerEvent keyEvent in onKeyTriggers) { bool active = false; if (keyEvent.TriggerType == VRC_Trigger.TriggerType.OnKeyDown) { active = Input.GetKeyDown(keyEvent.Key); } else { active = Input.GetKeyUp(keyEvent.Key); } if (active) { eventsToFire.Add(keyEvent); } } } private void OnEnable() { if (Trigger == null) { return; } AddListeners(); } private void OnDisable() { if (Trigger == null) { return; } RemoveListeners(); } private void AddListeners() { if (hasAddedListeners) { return; } hasAddedListeners = true; if (onKeyTriggers.Count > 0) { CyanEmuTriggerExecutor.AddKeyTrigger(this); } if (timerTriggers.Count > 0) { CyanEmuTriggerExecutor.AddTimerTrigger(this); } } private void RemoveListeners() { if (!hasAddedListeners) { return; } hasAddedListeners = false; if (onKeyTriggers.Count > 0) { CyanEmuTriggerExecutor.RemoveKeyTrigger(this); } if (timerTriggers.Count > 0) { CyanEmuTriggerExecutor.RemoveTimerTrigger(this); } } private void OnDestroy() { CyanEmuTriggerExecutor.RemoveTrigger(Trigger); } #region ICyanEmuInteractable public float GetProximity() { return Trigger.proximity; } public bool CanInteract() { return Trigger.enabled && interactTriggers.Count > 0; } public string GetInteractText() { return Trigger.interactText; } public void Interact() { Trigger.Interact(); } #endregion #region ICyanEmuPickupable public void OnPickup() { Trigger.ExecuteTriggerType(VRC_Trigger.TriggerType.OnPickup); } public void OnDrop() { Trigger.ExecuteTriggerType(VRC_Trigger.TriggerType.OnDrop); } public void OnPickupUseDown() { Trigger.ExecuteTriggerType(VRC_Trigger.TriggerType.OnPickupUseDown); } public void OnPickupUseUp() { Trigger.ExecuteTriggerType(VRC_Trigger.TriggerType.OnPickupUseUp); } #endregion #region ICyanEmuStationHandler public void OnStationEnter(VRC.SDKBase.VRCStation station) { VRC_Trigger.TriggerCustom((station as VRCSDK2.VRC_Station).OnLocalPlayerEnterStation); Trigger.ExecuteTriggerType(VRC_Trigger.TriggerType.OnStationEntered); } public void OnStationExit(VRC.SDKBase.VRCStation station) { VRC_Trigger.TriggerCustom((station as VRCSDK2.VRC_Station).OnLocalPlayerExitStation); Trigger.ExecuteTriggerType(VRC_Trigger.TriggerType.OnStationExited); } #endregion } } #endif