using System; using System.Collections.Generic; using System.Linq; using _PROJECT.Scripts.Bow; using FishNet.Object; using FishNet.Object.Synchronizing; using TMPro; using Unity.XR.CoreUtils; using UnityEngine; using Random = UnityEngine.Random; public class ArcheryRange : NetworkBehaviour { public TMP_Text highScoreText; public TMP_Text timeLeftText; public TMP_Text scoreText; public Transform targetStartPosition; public Transform targetEndPosition; public GameObject targetPrefab; public StartTarget startTarget; public Vector3 minRandomOffset = new Vector3(0f, -2f, -5f); public Vector3 maxRandomOffset = new Vector3(0f, 2f, 5f); public float roundLength = 60f; public float targetSpawnTime = 3f; public KeyboardManager keyboardManager; private List _targets; [SyncVar] private float _score; private float _maxScore; private float _roundEndTime; private float _nextTargetTime; private bool _roundActive; private readonly List _presentPlayers = new(); private XROrigin _scoredPlayer; public override void OnStartServer() { base.OnStartServer(); _roundActive = false; _targets = new List(); _score = 0; _maxScore = 0; _roundEndTime = 0f; _maxScore = LoadScoreFromDisk(); //+Debug.Log($"highScore is {_maxScore}"); StartCoroutine(DelayedSetHighScoreText(10f)); // Cannot immediately assign these values because fishnet needs time. } private System.Collections.IEnumerator DelayedSetHighScoreText(float delaySeconds) { yield return new WaitForSeconds(delaySeconds); SetHighScoreText($"High Score: {_maxScore}"); } private void OnTriggerEnter(Collider other) { SetHighScoreText($"High Score: {_maxScore}"); // Refresh high score UI XROrigin enteredPlayer = other.GetComponent(); if (enteredPlayer == null || _presentPlayers.Contains(enteredPlayer)) return; _presentPlayers.Add(enteredPlayer); // Adds to the end } private void OnTriggerExit(Collider other) { XROrigin exitedPlayer = other.GetComponent(); if (exitedPlayer == null) return; _presentPlayers.Remove(exitedPlayer); // Shifts others left automatically if (exitedPlayer == _scoredPlayer) keyboardManager.DeActivate(); // If the player īrefuses to enter their name. } public override void OnStopServer() { base.OnStopServer(); SaveScoreToDisk(_maxScore); } // Update is called once per frame void Update() { if (!IsServer) return; if (!_roundActive) return; if (Time.time >= _roundEndTime) { ResetRange(); } else { SetTimeLeftText($"Time Left: {Math.Ceiling((_roundEndTime - Time.time) % 60)}"); if (Time.time >= _nextTargetTime) { _nextTargetTime = Time.time + targetSpawnTime; SpawnTarget(); } } } private void SpawnTarget() { if (!IsServer) return; var randomPos = targetStartPosition.position + new Vector3( Random.Range(minRandomOffset.x, maxRandomOffset.x), (float)Math.Round(Random.Range(minRandomOffset.y, maxRandomOffset.y)), Random.Range(minRandomOffset.z, maxRandomOffset.z)); var target = SpawnTarget(randomPos); _targets.Add(target); } private ArcheryTarget SpawnTarget(Vector3 randomPos) { var prefab = Instantiate(targetPrefab, randomPos, Quaternion.identity, null); ArcheryTarget target = prefab.GetComponent(); target.endPosition = targetEndPosition.position; target.addScore = AddScore; Spawn(prefab); return target; } public void ResetRange() { if (!IsServer) return; foreach (var target in _targets.Where(target => target != null)) { Despawn(target.gameObject, DespawnType.Pool); } _targets = new List(); if (_maxScore < _score) _maxScore = _score; if(_presentPlayers.Count != 0) // If there are players in the area. { // Gives the score to the player longest-lasting in the area. It would be better to give it to the player that fired the starting arrow, but I'm not spending 10 hours on this. XROrigin scoringPlayer = _presentPlayers.First(); Menu playermenu = scoringPlayer.GetComponentInChildren(); _scoredPlayer = scoringPlayer; playermenu.setCanvasVisibility(false); // Deactivates the menu to avoid overlap issues. Transform keyboardPlacement = playermenu.updateMenuTransform(); keyboardManager.Activate(_score, keyboardPlacement); SaveScoreToDisk(_maxScore); SetHighScoreText($"High Score: {_maxScore}"); } _score = 0; startTarget.ShowTarget(); _roundActive = false; SetTimeLeftText(""); } public void StartRound() { if (!IsServer) return; _roundEndTime = Time.time + roundLength; _nextTargetTime = Time.time; _roundActive = true; } public void AddScore(float distance) { if (!IsServer) return; _score += distance; SetScoreText($"Score: {_score}"); } [ObserversRpc] public void SetHighScoreText(string text) { highScoreText.text = text; } [ObserversRpc] public void SetScoreText(string text) { scoreText.text = text; } [ObserversRpc] public void SetTimeLeftText(string text) { timeLeftText.text = text; } public class SaveData { public float HighScore; } public void SaveScoreToDisk(float highScore) { SaveData data = new SaveData { HighScore = highScore }; string json = JsonUtility.ToJson(data); System.IO.File.WriteAllText("highscore.json", json); } public float LoadScoreFromDisk() { if (System.IO.File.Exists("highscore.json")) { string json = System.IO.File.ReadAllText("highscore.json"); SaveData data = JsonUtility.FromJson(json); return data.HighScore; } return -1f; // default score } }