using MMMaellon.LightSync; using UdonSharp; using UnityEngine; using VRC.SDK3.Components; using VRC.SDK3.Data; using VRC.SDK3.UdonNetworkCalling; using VRC.SDKBase; using VRC.Udon.Common.Interfaces; [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class FloorMapMarker : UdonSharpBehaviour { [SerializeField] private GameManagerRound3 _GameManager; [SerializeField] private Material _UnlitLampMaterial; [SerializeField] private Material _LitLampMaterial; [UdonSynced, SerializeField, FieldChangeCallback(nameof(Active))] private bool _Active = false; [UdonSynced, FieldChangeCallback(nameof(IsGrabbed))] private bool _IsGrabbed = false; [UdonSynced, FieldChangeCallback(nameof(IsLit))] private bool _IsLit = false; [UdonSynced] private bool _EnableCollisionChecks = false; [UdonSynced] private bool _UsedUp = false; private int _CollisionCheckCounter = 0; private int _NoCollisionCounter = 0; private DataList _CollidingLocations = new DataList(); private Rigidbody _RigidBodyComponent; private LightSync _ObjectSync; private VRCPickup _PickupComponent; private MeshRenderer _MarkerMesh; private VRCPlayerApi _CurrentOwner; private const int MAX_REPEAT_COLLISION_CHECKS = 3; private const int MAX_CHECKS_WITH_NO_COLLISIONS = 6; private const float TIME_BETWEEN_REPEAT_COLLISION_CHECKS = 0.15f; void Start() { _RigidBodyComponent = GetComponent(); _ObjectSync = GetComponent(); _PickupComponent = GetComponent(); _MarkerMesh = GetComponent(); _CurrentOwner = Networking.GetOwner(gameObject); } public override void OnOwnershipTransferred(VRCPlayerApi Player) { _CurrentOwner = Player; base.OnOwnershipTransferred(Player); } [NetworkCallable] public void Initialise() { _RigidBodyComponent.constraints = RigidbodyConstraints.None; _ObjectSync.TeleportToLocalSpace(Vector3.zero, Quaternion.identity, true); SetPickupable(false); Active = false; IsGrabbed = false; IsLit = false; _EnableCollisionChecks = false; _UsedUp = false; _CollisionCheckCounter = 0; _NoCollisionCounter = 0; _CollidingLocations.Clear(); RequestSerialization(); } [NetworkCallable] public void SetPickupable(bool Pickupable) { _PickupComponent.pickupable = Pickupable; } public void OnTriggerEnter(Collider OtherCollider) { FloorMapLocation Location = OtherCollider.GetComponent(); if (Location != null) { _CollidingLocations.Add(Location); _NoCollisionCounter = 0; } } public void OnTriggerExit(Collider OtherCollider) { FloorMapLocation Location = OtherCollider.GetComponent(); if (Location != null) { _CollidingLocations.Remove(Location); } } public override void OnPickup() { IsGrabbed = true; base.OnPickup(); } public override void OnDrop() { IsGrabbed = false; base.OnDrop(); } private void SwapLampMaterial() { Material[] Materials = _MarkerMesh.materials; Materials[2] = IsLit ? _LitLampMaterial : _UnlitLampMaterial; _MarkerMesh.materials = Materials; } private void ReactToGrab() { if (IsGrabbed) { _RigidBodyComponent.constraints = RigidbodyConstraints.None; _EnableCollisionChecks = true; _NoCollisionCounter = 0; _CollidingLocations.Clear(); } else { VRCPlayerApi Owner = Networking.GetOwner(gameObject); if (!Owner.IsUserInVR()) { transform.eulerAngles = new Vector3(0.0f, transform.eulerAngles.y, 0.0f); } _NoCollisionCounter = 0; } RequestSerialization(); } public void CheckCollisionsLoop() { if (Active) { SendCustomEventDelayedSeconds(nameof(CheckCollisionsLoop), TIME_BETWEEN_REPEAT_COLLISION_CHECKS); } if (_EnableCollisionChecks && !IsGrabbed) { if (_CollidingLocations.Count > 0 && IsUpright()) { _NoCollisionCounter = 0; for (int i = 0; i < _CollidingLocations.Count; i++) { FloorMapLocation Location = (FloorMapLocation)_CollidingLocations[i].Reference; if (Location != null) { bool FoundCorrectResponse = ConfirmChoice(Location.Country, Location.City); if (FoundCorrectResponse) { SendCorrectResponse(Location.transform.position); return; } } } _CollisionCheckCounter++; if (_CollisionCheckCounter >= MAX_REPEAT_COLLISION_CHECKS) { SendIncorrectResponse(); } } else { _NoCollisionCounter++; if (_NoCollisionCounter >= MAX_CHECKS_WITH_NO_COLLISIONS) { SendIncorrectResponse(); } } } } public bool ConfirmChoice(string Country, string City) { if (Country == _GameManager.GetCurrentCountry() && City == _GameManager.GetCurrentCity()) { return true; } return false; } private void SendCorrectResponse(Vector3 CorrectLocation) { DisableMovementCompletely(CorrectLocation); IsLit = true; Active = false; _EnableCollisionChecks = false; _UsedUp = true; RequestSerialization(); if (_CurrentOwner == Networking.LocalPlayer) { _GameManager.SendCustomNetworkEvent(NetworkEventTarget.Owner, "CorrectResponse"); } } private void SendIncorrectResponse() { DisableMovementCompletely(); _CollisionCheckCounter = 0; _CollidingLocations.Clear(); _EnableCollisionChecks = false; if (_CurrentOwner == Networking.LocalPlayer) { _GameManager.SendCustomNetworkEvent(NetworkEventTarget.Owner, "IncorrectResponse"); } RequestSerialization(); } [NetworkCallable] public void Activated(bool SetActive) { Active = SetActive; RequestSerialization(); } private void ReactToActivation() { _PickupComponent.pickupable = Active; SendCustomEventDelayedSeconds(nameof(CheckCollisionsLoop), TIME_BETWEEN_REPEAT_COLLISION_CHECKS); } public bool IsUsedUp() { return _UsedUp; } private void DisableMovementCompletely(Vector3 CorrectLocation = new Vector3()) { _RigidBodyComponent.constraints = RigidbodyConstraints.FreezeAll; transform.eulerAngles = new Vector3(0.0f, transform.eulerAngles.y, 0.0f); if (CorrectLocation != Vector3.zero) { transform.position = CorrectLocation; } RequestSerialization(); } private bool IsUpright() { return (Vector3.Dot(transform.up, Vector3.up) >= 0.85f); } private bool Active { set { _Active = value; ReactToActivation(); } get => _Active; } private bool IsGrabbed { set { _IsGrabbed = value; ReactToGrab(); } get => _IsGrabbed; } private bool IsLit { set { _IsLit = value; SwapLampMaterial(); } get => _IsLit; } }