using System; using System.Collections.Generic; using BlockSpace.Voxels; using Photon.VR.Player; using UnityEngine; [DisallowMultipleComponent] public sealed class TriggerBehavior : NodeBehavior { private const float PlayerTouchRadius = 0.12f; private const float StandingRayLift = 0.15f; private const float StandingRayDistance = 1.5f; private const float PlayerCacheRefreshInterval = 0.25f; private static readonly Collider[] TouchOverlapBuffer = new Collider[16]; private readonly HashSet touchingPlayerIds = new HashSet(); private readonly HashSet currentFramePlayerIds = new HashSet(); private readonly List cachedPlayers = new List(); private VoxelGroup targetGroup; private TriggerTargetCollisionRelay collisionRelay; private float nextPlayerCacheRefreshAt; private void Start() { if (base.IsAuthoritative) { RefreshTargetGroup(force: true); } } private void OnDisable() { UnbindCollisionRelay(); touchingPlayerIds.Clear(); currentFramePlayerIds.Clear(); cachedPlayers.Clear(); } private void Update() { if (base.IsAuthoritative) { RefreshTargetGroup(force: false); PollPlayerTouches(); } } public override void OnInputValueChanged(string inputPortName, BSValue value) { if (base.IsAuthoritative && string.Equals(inputPortName, "Object", StringComparison.Ordinal)) { RefreshTargetGroup(force: true); } } private void RefreshTargetGroup(bool force) { VoxelGroup inputGroup = GetInputGroup("Object"); if (!force && targetGroup == inputGroup) { return; } if (collisionRelay != null) { collisionRelay.ColliderEntered -= HandleColliderEntered; } targetGroup = inputGroup; touchingPlayerIds.Clear(); currentFramePlayerIds.Clear(); collisionRelay = null; if (targetGroup == null) { SetOutputValueIfChanged("Player", BSValue.None); SetOutputValueIfChanged("Object", BSValue.None); return; } collisionRelay = targetGroup.GetComponent(); if (collisionRelay == null) { collisionRelay = targetGroup.gameObject.AddComponent(); } collisionRelay.ColliderEntered += HandleColliderEntered; } private void UnbindCollisionRelay() { if (collisionRelay != null) { collisionRelay.ColliderEntered -= HandleColliderEntered; collisionRelay = null; } } private void PollPlayerTouches() { if (targetGroup == null) { touchingPlayerIds.Clear(); return; } if (Time.unscaledTime >= nextPlayerCacheRefreshAt) { RefreshPlayerCache(); nextPlayerCacheRefreshAt = Time.unscaledTime + 0.25f; } currentFramePlayerIds.Clear(); for (int i = 0; i < cachedPlayers.Count; i++) { PhotonVRPlayer photonVRPlayer = cachedPlayers[i]; if (photonVRPlayer == null || !photonVRPlayer.gameObject.activeInHierarchy) { continue; } int playerId = GetPlayerId(photonVRPlayer); if (playerId != 0 && IsPlayerTouchingTarget(photonVRPlayer, targetGroup)) { currentFramePlayerIds.Add(playerId); if (!touchingPlayerIds.Contains(playerId)) { touchingPlayerIds.Add(playerId); SetOutputValueIfChanged("Player", BSValue.FromPlayer(playerId)); TriggerOutput("Touched"); } } } touchingPlayerIds.RemoveWhere((int item) => !currentFramePlayerIds.Contains(item)); } private void HandleColliderEntered(Collider other) { if (!base.IsAuthoritative || targetGroup == null || other == null || TryResolvePlayer(other, out var _)) { return; } VoxelGroup componentInParent = other.GetComponentInParent(); if (!(componentInParent == targetGroup)) { if (componentInParent != null) { SetOutputValueIfChanged("Object", BSValue.FromGroup(componentInParent.id)); } else { SetOutputValueIfChanged("Object", BSValue.None); } TriggerOutput("Object Touched"); } } private void RefreshPlayerCache() { cachedPlayers.Clear(); PhotonVRPlayer[] array = UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); for (int i = 0; i < array.Length; i++) { if (array[i] != null) { cachedPlayers.Add(array[i]); } } } private static bool IsPlayerTouchingTarget(PhotonVRPlayer player, VoxelGroup group) { if (player == null || group == null) { return false; } Collider collider = ((group.meshCollider != null && group.meshCollider.enabled) ? group.meshCollider : group.GetComponent()); if (collider == null || !collider.enabled) { return false; } if (IsPointTouchingCollider(collider, player.Head, 0.12f) || IsPointTouchingCollider(collider, player.LeftHand, 0.12f) || IsPointTouchingCollider(collider, player.RightHand, 0.12f)) { return true; } return IsStandingOnCollider(collider, player); } private static bool IsPointTouchingCollider(Collider targetCollider, Transform pointTransform, float radius) { if (targetCollider == null || pointTransform == null) { return false; } Vector3 position = pointTransform.position; float num = radius * radius; if (targetCollider.bounds.SqrDistance(position) > num) { return false; } int num2 = Physics.OverlapSphereNonAlloc(position, radius, TouchOverlapBuffer, -1, QueryTriggerInteraction.Collide); for (int i = 0; i < num2; i++) { if (TouchOverlapBuffer[i] == targetCollider) { return true; } } return false; } private static bool IsStandingOnCollider(Collider targetCollider, PhotonVRPlayer player) { if (targetCollider == null || player == null) { return false; } Transform transform = ((player.Body != null) ? player.Body : player.Head); if (transform == null) { return false; } if (!Physics.Raycast(new Ray(transform.position + Vector3.up * 0.15f, Vector3.down), out var hitInfo, 1.5f, -5, QueryTriggerInteraction.Ignore)) { return false; } if (hitInfo.collider == targetCollider && hitInfo.distance > 0.05f) { return hitInfo.normal.y >= 0.35f; } return false; } private static bool TryResolvePlayer(Collider collider, out PhotonVRPlayer player) { player = ((collider != null) ? collider.GetComponentInParent() : null); return player != null; } private static int GetPlayerId(PhotonVRPlayer player) { if (!(player != null) || !(player.photonView != null) || player.photonView.Owner == null) { return 0; } return player.photonView.Owner.ActorNumber; } }