CarmenSandiego/Assets/UdonSharp/Maps/FloorMapMarker.cs
Jamie Greunbaum 999a0342d5 - Keeping logs of previous round winners so player names can be referenced.
- Round 3 sync seems to work now, but resets and early grabs cause issues.
2025-07-18 17:04:02 -04:00

309 lines
6.6 KiB
C#

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 _LocationFindingEnabled = false;
[UdonSynced] private int _FailureCounter = 0;
[UdonSynced] private bool _CheckingCollisions = 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 const int MAX_FAILURE_COUNT = 2;
private const int MAX_REPEAT_COLLISION_CHECKS = 3;
private const int MAX_CHECKS_WITH_NO_COLLISIONS = 8;
private const float TIME_BETWEEN_REPEAT_COLLISION_CHECKS = 0.15f;
void Start()
{
_RigidBodyComponent = GetComponent<Rigidbody>();
_ObjectSync = GetComponent<LightSync>();
_PickupComponent = GetComponent<VRCPickup>();
_MarkerMesh = GetComponent<MeshRenderer>();
}
public void Initialise()
{
_ObjectSync.TeleportToLocalSpace(Vector3.zero, Quaternion.identity, true);
SetPickupable(false);
Active = false;
IsGrabbed = false;
IsLit = false;
_LocationFindingEnabled = false;
_CheckingCollisions = false;
_FailureCounter = 0;
_CollisionCheckCounter = 0;
_NoCollisionCounter = 0;
_CollidingLocations.Clear();
RequestSerialization();
}
public void SetPickupable(bool Pickupable)
{
_PickupComponent.pickupable = Pickupable;
}
public void OnTriggerEnter(Collider OtherCollider)
{
FloorMapLocation Location = OtherCollider.GetComponent<FloorMapLocation>();
if (Location != null)
{
_CollidingLocations.Add(Location);
_NoCollisionCounter = 0;
}
}
public void OnTriggerExit(Collider OtherCollider)
{
FloorMapLocation Location = OtherCollider.GetComponent<FloorMapLocation>();
if (Location != null)
{
_CollidingLocations.Remove(Location);
}
}
public override void OnPickup()
{
if (Active)
{
IsGrabbed = true;
}
base.OnPickup();
}
public override void OnDrop()
{
if (Active)
{
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;
_NoCollisionCounter = 0;
_CollidingLocations.Clear();
}
else
{
_LocationFindingEnabled = true;
VRCPlayerApi Owner = Networking.GetOwner(gameObject);
if (!Owner.IsUserInVR())
{
transform.eulerAngles = new Vector3(0.0f, transform.eulerAngles.y, 0.0f);
}
_NoCollisionCounter = 0;
if (!_CheckingCollisions) SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(CheckCollisions));
}
RequestSerialization();
}
[NetworkCallable]
public void CheckCollisions()
{
if (!Active || !_LocationFindingEnabled || IsGrabbed) return;
_CheckingCollisions = true;
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;
}
}
}
// We should make sure to only reach here if we don't get a correct
// response from the previous loop.
if (_CollisionCheckCounter < MAX_REPEAT_COLLISION_CHECKS)
{
SendCustomEventDelayedSeconds(nameof(CheckCollisions), TIME_BETWEEN_REPEAT_COLLISION_CHECKS);
}
else
{
SendIncorrectResponse();
}
_CollisionCheckCounter++;
}
else
{
_NoCollisionCounter++;
if (_NoCollisionCounter >= MAX_CHECKS_WITH_NO_COLLISIONS)
{
SendIncorrectResponse();
}
else
{
// If the marker isn't sitting mostly upright and is not grabbed,
// don't check collisions until later, in case it becomes upright.
SendCustomEventDelayedSeconds(nameof(CheckCollisions), TIME_BETWEEN_REPEAT_COLLISION_CHECKS);
}
}
}
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;
_CheckingCollisions = false;
RequestSerialization();
_GameManager.SendCustomNetworkEvent(NetworkEventTarget.Owner, "CorrectResponse");
_GameManager.PlayCorrectSound();
}
private void SendIncorrectResponse()
{
DisableMovementCompletely();
_CollisionCheckCounter = 0;
_CheckingCollisions = false;
_CollidingLocations.Clear();
_FailureCounter++;
if (_FailureCounter >= MAX_FAILURE_COUNT)
{
Active = false;
_GameManager.SendCustomNetworkEvent(NetworkEventTarget.Owner, "IncorrectResponse");
}
_GameManager.PlayIncorrectSound();
RequestSerialization();
}
[NetworkCallable]
public void Activated(bool SetActive)
{
Active = SetActive;
RequestSerialization();
}
private void ChangePickupable()
{
_PickupComponent.pickupable = Active;
}
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;
}
_LocationFindingEnabled = false;
RequestSerialization();
}
private bool IsUpright()
{
return (Vector3.Dot(transform.up, Vector3.up) >= 0.85f);
}
private bool Active
{
set
{
_Active = value;
ChangePickupable();
}
get => _Active;
}
private bool IsGrabbed
{
set
{
_IsGrabbed = value;
ReactToGrab();
}
get => _IsGrabbed;
}
private bool IsLit
{
set
{
_IsLit = value;
SwapLampMaterial();
}
get => _IsLit;
}
}