235 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| 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<ArcheryTarget> _targets;
 | ||
|     
 | ||
|     [SyncVar]
 | ||
|     private float _score;
 | ||
|     private float _maxScore;
 | ||
|     private float _roundEndTime;
 | ||
|     private float _nextTargetTime;
 | ||
|     private bool _roundActive;
 | ||
| 
 | ||
|     private readonly List<XROrigin> _presentPlayers = new();
 | ||
| 
 | ||
|     private XROrigin _scoredPlayer;
 | ||
| 
 | ||
|     public override void OnStartServer()
 | ||
|     {
 | ||
|         base.OnStartServer();
 | ||
|         _roundActive = false;
 | ||
|         _targets = new List<ArcheryTarget>();
 | ||
|         _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<XROrigin>();
 | ||
|         if (enteredPlayer == null || _presentPlayers.Contains(enteredPlayer))
 | ||
|             return;
 | ||
| 
 | ||
|         _presentPlayers.Add(enteredPlayer); // Adds to the end
 | ||
|     }
 | ||
| 
 | ||
|     private void OnTriggerExit(Collider other)
 | ||
|     {
 | ||
|         XROrigin exitedPlayer = other.GetComponent<XROrigin>();
 | ||
|         if (exitedPlayer == null) return;
 | ||
| 
 | ||
|         _presentPlayers.Remove(exitedPlayer); // Shifts others left automatically
 | ||
| 
 | ||
|         if (exitedPlayer == _scoredPlayer) keyboardManager.DeActivate(); // If the player <20>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<ArcheryTarget>();
 | ||
|         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<ArcheryTarget>();
 | ||
|         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<Menu>();
 | ||
| 
 | ||
|             _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<SaveData>(json);
 | ||
|             return data.HighScore;
 | ||
|         }
 | ||
| 
 | ||
|         return 0f; // default score
 | ||
|     }
 | ||
| 
 | ||
| }
 | ||
| 
 |