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 -1f; // default score
|
||
}
|
||
|
||
}
|
||
|