You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

602 lines
17 KiB

using ExitGames.Client.Photon;
using Photon.Pun;
using Photon.Realtime;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.EventSystems;
public class Game : MonoBehaviourPunCallbacks {
//Singleton
public static Game Instance { get; private set; }
//General information
[Header("Game flow")]
[Tooltip("Play time of the game in minutes")]
public float playTime;
[Tooltip("Length of overtime in seconds")]
public float overtime;
[Tooltip("Tick rate in ticks/second")]
public float tickRate;
[Header("Spawn Information")]
public GameObject[] circuitSpawns;
public float circuitScale;
public float connectionScale;
[Header("Prefabs")]
public List<CircuitPrefabGroup> circuitPrefabs;
public GameObject cablePrefab;
public GameObject chargePrefab;
//Temporary dragged
[HideInInspector]
public Circuit draggedCircuit;
[HideInInspector]
public Connection draggedConnection;
[HideInInspector]
public Connection draggedCutter;
//Necessary references
private PhotonView pv;
public Camera cam;
//Objects
public List<Circuit> Circuits { get; } = new List<Circuit>();
private readonly List<Circuit> circuitsToRemove = new List<Circuit>();
public List<Connection> Connections { get; } = new List<Connection>();
private readonly List<Connection> connectionsToRemove = new List<Connection>();
//Called by Unity
void Awake() {
Instance = this;
pv = GetComponent<PhotonView>();
}
void Start() {
if (!PhotonNetwork.IsConnected) {
PhotonNetwork.OfflineMode = true;
PhotonNetwork.CreateRoom("development");
PhotonNetwork.LocalPlayer.SetCustomProperties(new Hashtable() { ["ColorIndex"] = Random.Range(1, 9) });
StartGame();
}
}
void Update() {
if (Input.GetMouseButtonDown(0) && !EventSystem.current.IsPointerOverGameObject())
OnLeftPressed();
if (Input.GetMouseButtonDown(1) && !EventSystem.current.IsPointerOverGameObject())
OnRightPressed();
else if (Input.GetMouseButtonUp(0))
OnLeftReleased();
else if (Input.GetMouseButton(0))
MoveDragged();
if (IsPlaying)
UpdateGame();
}
//Events and input controls
public void OnDragStopped() {
if (draggedCircuit != null) {
Destroy(draggedCircuit.gameObject);
draggedCircuit = null;
}
if (draggedConnection != null) {
Destroy(draggedConnection.gameObject);
draggedConnection = null;
}
if (draggedCutter != null) {
Destroy(draggedCutter.gameObject);
draggedCutter = null;
}
}
private void OnLeftPressed() {
if (!Circuits.Exists(c => c.IsMouseOver) && IsPlaying) {
if (ScreenToPlaneRayCast(Input.mousePosition, out Vector3 hitPoint))
SpawnDraggableCutter(hitPoint);
}
}
private void OnRightPressed() {
OnDragStopped();
}
private void OnLeftReleased() {
if (draggedCircuit != null) {
if (!draggedCircuit.IsNeedingReplace && !EventSystem.current.IsPointerOverGameObject()) {
Vector3 pos = draggedCircuit.transform.position;
CircuitType type = draggedCircuit.type;
if (SpawnGameCircuit(pos, type)) {
draggedCircuit.uICircuitCount.Count--;
}
}
}
if (draggedConnection != null) {
Circuit receiver = draggedConnection.Receiver;
if (receiver != null && draggedConnection.collidedCircuits.Count == 2) {
Circuit sender = draggedConnection.Sender;
Vector3 pos = draggedConnection.transform.position;
SpawnGameConnection(sender, receiver, pos);
}
}
if (draggedCutter != null) {
List<Connection> delete = new List<Connection>();
foreach (Connection connection in Connections.FindAll(c => c.IsMine)) {
if (connection.collidedConnections.Contains(draggedCutter))
delete.Add(connection);
}
foreach (Connection connection in delete)
DestroyConnection(connection);
}
OnDragStopped();
}
private void MoveDragged() {
if (ScreenToPlaneRayCast(Input.mousePosition, out Vector3 point, true)) {
if (draggedCircuit != null) {
draggedCircuit.transform.position = point;
bool isOut = !ScreenToPlaneRayCast(Input.mousePosition, out point);
draggedCircuit.IsOutOfGamePlane = isOut;
}
if (draggedConnection != null)
AdjustDraggedConnection(draggedConnection);
if (draggedCutter != null)
AdjustDraggedConnection(draggedCutter, true);
}
}
private void AdjustDraggedConnection(Connection dragged, bool cutter = false) {
Vector3 p1 = dragged.rootPosition;
if (ScreenToPlaneRayCast(Input.mousePosition, out Vector3 p2)) {
if (!cutter) {
float range = dragged.Sender.Values.range;
Circuit potencialReceiver = Circuits.Find(
c => c.IsMouseOver && c.id != dragged.Sender.id &&
Vector3.Distance(p1, c.transform.position) <= range
);
dragged.Receiver = potencialReceiver;
if (potencialReceiver != null)
p2 = potencialReceiver.transform.position;
if (Vector3.Distance(p1, p2) > range) {
Vector3 dir = (p2 - p1).normalized * range;
p2 = p1 + dir;
}
}
float scale = Vector3.Distance(p1, p2) * 4;
Vector3 pos = (p1 + p2) / 2;
Quaternion rot = Quaternion.FromToRotation(Vector3.up, p2 - p1);
dragged.LengthScale = scale;
dragged.transform.SetPositionAndRotation(pos, rot);
}
}
//Gameplay
public bool IsPlaying { get; private set; }
private bool isOvertime;
private float currentPlayTime;
private float currentOvertime;
public void StartGame() {
GameGUI.Instance.StartGUI();
SpawnCircuit();
IsPlaying = true;
Debug.Log("Game has started");
}
private void UpdateGame() {
if (PhotonNetwork.IsMasterClient) {
foreach (Circuit circuit in Circuits)
circuit.UpdateCircuit(tickRate);
foreach (Connection connection in Connections)
connection.UpdateConnection(tickRate);
}
foreach (Circuit circuit in circuitsToRemove) {
Circuits.Remove(circuit);
Destroy(circuit.gameObject);
}
circuitsToRemove.Clear();
foreach (Connection connection in connectionsToRemove) {
Connections.Remove(connection);
Destroy(connection.gameObject);
}
connectionsToRemove.Clear();
UpdateTime();
}
private void UpdateTime() {
float textTime;
if (isOvertime) {
currentOvertime += Time.deltaTime;
if (currentOvertime >= overtime) {
currentOvertime = overtime;
if (PhotonNetwork.IsMasterClient)
OnTimeRunnedOut();
}
textTime = overtime - currentOvertime;
}
else {
currentPlayTime += Time.deltaTime;
if (currentPlayTime >= playTime * 60) {
currentPlayTime = playTime * 60;
if (PhotonNetwork.IsMasterClient)
OnTimeRunnedOut();
}
textTime = playTime * 60 - currentPlayTime;
}
GameGUI.Instance.SetTimeText(textTime);
}
private void OnTimeRunnedOut() {
float[] charges = new float[5];
foreach (Circuit circuit in Circuits) {
charges[circuit.ActorNumber] += circuit.Charge;
}
int greatestIndex = 0;
for (int i = 0; i < charges.Length; i++) {
float greatest = charges[greatestIndex];
if (charges[i] > greatest)
greatestIndex = i;
}
bool isDraw(out List<int> winners) {
winners = new List<int>();
for (int i = 0; i < charges.Length; i++) {
if (charges[i] == charges[greatestIndex]) {
winners.Add(i);
}
}
if (winners.Count > 1)
return true;
return false;
}
if (isDraw(out List<int> winnerActorNumbers)) {
//Draw
pv.RPC("RpcStartNewOvertime", RpcTarget.All, winnerActorNumbers.ToArray());
} else {
//Unique winner
pv.RPC("RpcFinishGame", RpcTarget.All, greatestIndex);
}
}
[PunRPC]
private void RpcStartNewOvertime(int[] winnerActorNumbers) {
if (isOvertime)
currentOvertime = 0;
isOvertime = true;
Debug.Log("The players " + string.Join(",", winnerActorNumbers) + " triggered an overtime!");
}
[PunRPC]
private void RpcFinishGame(int winnerActorNumber) {
OnDragStopped();
GameGUI.Instance.ShowWinnerBannerPanel(winnerActorNumber);
GameGUI.Instance.CloseCircuitMenu();
IsPlaying = false;
Debug.Log("Player " + winnerActorNumber + " won the game!");
}
//Circuit Management
public void SendCircuitCharge(int circuitId, float charge) {
pv.RPC("RpcApplyCircuitCharge", RpcTarget.Others, circuitId, charge);
}
[PunRPC]
private void RpcApplyCircuitCharge(int circuitId, float charge) {
Circuit circuit = GetCircuitById(circuitId);
if (circuit == null) {
Debug.LogWarning("Circuit to be updated is null");
return;
}
circuit.Charge = charge;
}
public void SpawnDraggableCircuit(Vector3 screenPos, CircuitType type, UICircuitCount uICIrcuitCount) {
if (ScreenToPlaneRayCast(screenPos, out Vector3 hitPoint, true)) {
Circuit circuit = SpawnCircuit(hitPoint, type, 1);
circuit.ActorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
circuit.isDragged = true;
circuit.uICircuitCount = uICIrcuitCount;
draggedCircuit = circuit;
}
}
public bool SpawnGameCircuit(Vector3 pos, CircuitType type, int level = 1) {
Vector3 screenPos = cam.WorldToScreenPoint(pos);
if (ScreenToPlaneRayCast(screenPos, out Vector3 hitPoint)) {
SendSpawnCircuit(hitPoint, type, level);
return true;
}
return false;
}
private void SpawnCircuit() {
int index = PhotonNetwork.PlayerList.ToList<Player>().IndexOf(PhotonNetwork.LocalPlayer);
Vector3 pos = circuitSpawns[index].transform.position;
CircuitType type = CircuitType.Basic;
int level = 2;
float charge = GameManager.Instance.restPoints;
SendSpawnCircuit(pos, type, level, charge);
}
private void SendSpawnCircuit(Vector3 pos, CircuitType type, int level, float charge = 0) {
int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
int id = CreateCircuitId();
pv.RPC("RpcSpawnCircuit", RpcTarget.All, pos, type, level, charge, actorNumber, id);
}
private Circuit SpawnCircuit(Vector3 pos, CircuitType type, int level) {
Quaternion rot = Quaternion.Euler(0, 90, 0);
Transform parent = GameObject.Find("Circuits").transform;
Vector3 scale = Vector3.one * circuitScale;
CircuitPrefabGroup prefabGroup = circuitPrefabs.Find(x => x.type.Equals(type));
GameObject prefab = prefabGroup.GetValuesByLevel(level);
GameObject circuitObject = Instantiate(prefab, pos, rot, parent);
circuitObject.transform.localScale = scale;
Circuit circuit = circuitObject.GetComponent<Circuit>();
circuit.type = type;
circuit.level = level;
return circuit;
}
[PunRPC]
private Circuit RpcSpawnCircuit(Vector3 pos, CircuitType type, int level, float charge, int actorNumber, int id) {
Circuit circuit = SpawnCircuit(pos, type, level);
circuit.id = id;
circuit.ActorNumber = actorNumber;
circuit.Charge = charge;
Circuits.Add(circuit);
return circuit;
}
public void DestroyCircuit(Circuit circuit) {
pv.RPC("RpcDestroyCircuit", RpcTarget.AllViaServer, circuit.id);
}
[PunRPC]
private void RpcDestroyCircuit(int circuitId) {
Circuit circuit = GetCircuitById(circuitId);
if (circuit == null)
return;
foreach (Connection connection in circuit.GoingOutConnections) {
RpcDestroyConnection(connection.id);
}
List<Connection> friendlyInConnections =
circuit.GoingInConnections.FindAll(c => c.ActorNumber == circuit.ActorNumber);
foreach (Connection connection in friendlyInConnections) {
RpcDestroyConnection(connection.id);
}
circuitsToRemove.Add(circuit);
}
public void UpgradeCircuit(Circuit circuit) {
int newId = CreateCircuitId();
pv.RPC("RpcUpgradeCircuit", RpcTarget.AllViaServer, circuit.id, newId);
}
[PunRPC]
private void RpcUpgradeCircuit(int circuitId, int newId) {
Circuit oldCircuit = GetCircuitById(circuitId);
if (oldCircuit == null)
return;
//New values
Vector3 pos = oldCircuit.transform.position;
CircuitType type = oldCircuit.type;
int level = oldCircuit.level + 1;
float charge = oldCircuit.Charge * 0.8f;
int actorNumber = oldCircuit.ActorNumber;
Circuit newCircuit = RpcSpawnCircuit(pos, type, level, charge, actorNumber, newId);
foreach (Connection connection in oldCircuit.GoingOutConnections)
connection.Sender = newCircuit;
foreach (Connection connection in oldCircuit.GoingInConnections)
connection.Receiver = newCircuit;
RpcDestroyCircuit(oldCircuit.id);
if (oldCircuit.IsSelected)
newCircuit.Select();
}
public void SendUpgradeCharge(Circuit circuit) {
pv.RPC("RpcUpgradeCharge", RpcTarget.Others, circuit.UpgradeCharge, circuit.Charge, circuit.id);
}
[PunRPC]
private void RpcUpgradeCharge(float upgradeCharge, float charge, int circuitId) {
Circuit circuit = GetCircuitById(circuitId);
if (circuit == null)
return;
circuit.Charge = charge;
circuit.UpgradeCharge = upgradeCharge;
}
//Connection Management
private void SpawnDraggableCutter(Vector3 pos) {
Quaternion rot = Quaternion.Euler(90, 0, 0);
Connection cutter = SpawnConnection(null, null, pos, rot, 0);
cutter.ActorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
draggedCutter = cutter;
}
public void SpawnDraggableConnection(Vector3 pos, Circuit sender) {
Quaternion rot = Quaternion.Euler(90, 0, 0);
Connection connection = SpawnConnection(sender, null, pos, rot, 0);
draggedConnection = connection;
}
public void SpawnGameConnection(Circuit sender, Circuit receiver, Vector3 pos) {
Quaternion rot = draggedConnection.transform.rotation;
float lengthScale = draggedConnection.LengthScale;
SendSpawnConnection(sender, receiver, pos, rot, lengthScale);
}
private Connection SpawnConnection(Circuit sender, Circuit receiver, Vector3 pos, Quaternion rot, float lengthScale) {
Transform parent = GameObject.Find("Connections").transform;
GameObject connectionObject = Instantiate(cablePrefab, pos, rot, parent);
Connection connection = connectionObject.GetComponent<Connection>();
connection.Sender = sender;
connection.Receiver = receiver;
connection.LengthScale = lengthScale;
connection.rootPosition = pos;
return connection;
}
private void SendSpawnConnection(Circuit sender, Circuit receiver, Vector3 pos, Quaternion rot, float lengthScale) {
int senderId = sender.id;
int receiverId = receiver.id;
int id = CreateConnectionId();
pv.RPC("RpcSpawnConnection", RpcTarget.All, pos, rot, lengthScale, senderId, receiverId, id);
}
[PunRPC]
private void RpcSpawnConnection(Vector3 pos, Quaternion rot, float lengthScale, int senderId, int receiverId, int id) {
Circuit sender = GetCircuitById(senderId);
Circuit receiver = GetCircuitById(receiverId);
Connection connection = SpawnConnection(sender, receiver, pos, rot, lengthScale);
connection.id = id;
Connections.Add(connection);
GameGUI.Instance.UpdateMenuStats();
}
public void DestroyConnection(Connection connection) {
pv.RPC("RpcDestroyConnection", RpcTarget.AllViaServer, connection.id);
}
[PunRPC]
private void RpcDestroyConnection(int connectionId) {
Connection connection = GetConnectionById(connectionId);
if (connection == null)
return;
connectionsToRemove.Add(connection);
}
//Charge Management
public void SpawnConnectionCharge(Connection connection) {
int actorNumber = connection.ActorNumber;
int connectionId = connection.id;
int id = connection.CreateChargeId();
pv.RPC("RpcSpawnCharge", RpcTarget.All, connectionId, id, actorNumber);
}
[PunRPC]
private Charge RpcSpawnCharge(int connectionId, int id, int actorNumber) {
Connection connection = GetConnectionById(connectionId);
if (connection == null)
return null;
Charge charge = SpawnCharge(connection);
charge.id = id;
charge.ActorNumber = actorNumber;
connection.AddCharge(charge);
return charge;
}
private Charge SpawnCharge(Connection connection) {
Transform parent = connection.transform;
Vector3 pos = connection.Sender.transform.position;
Quaternion rot = parent.rotation;
Vector3 scale = new Vector3(1, 1 / parent.localScale.y * parent.localScale.x, 1);
GameObject chargeObject = Instantiate(chargePrefab, pos, rot, parent);
chargeObject.transform.localScale = scale;
Charge charge = chargeObject.GetComponent<Charge>();
charge.Speed = connection.Sender.Values.sendSpeed;
return charge;
}
public void DestroyCharge(Connection connection, Charge charge) {
int connectionId = connection.id;
int id = charge.id;
pv.RPC("RpcDestroyCharge", RpcTarget.All, connectionId, id);
}
[PunRPC]
private void RpcDestroyCharge(int connectionId, int id) {
Connection connection = GetConnectionById(connectionId);
if (connection == null)
return;
Charge charge = connection.GetChargeById(id);
connection.RemoveCharge(charge);
Destroy(charge.gameObject);
}
//Useful, logical functions
private bool ScreenToPlaneRayCast(Vector3 screenPos, out Vector3 hitPoint, bool outerPlane = false) {
Ray ray = cam.ScreenPointToRay(screenPos);
int layerMask;
if (outerPlane)
layerMask = LayerMask.GetMask("Outer Plane");
else
layerMask = LayerMask.GetMask("Game Plane");
if (Physics.Raycast(ray, out RaycastHit info, Mathf.Infinity, layerMask)) {
if (info.collider != null) {
hitPoint = info.point;
return true;
}
}
hitPoint = Vector3.zero;
return false;
}
private int CreateCircuitId() {
int id;
do
id = Random.Range(0, 10000);
while
(Circuits.Exists(c => c.id == id));
return id;
}
private int CreateConnectionId() {
int id;
do
id = Random.Range(0, 10000);
while
(Connections.Exists(c => c.id == id));
return id;
}
private Connection GetConnectionById(int id) {
Connection connection = Connections.Find(c => c.id == id);
return connection;
}
private Circuit GetCircuitById(int id) {
Circuit circuit = Circuits.Find(c => c.id == id);
return circuit;
}
public List<Circuit> CircuitsWithout(Circuit without) {
List<Circuit> copy = new List<Circuit>(Circuits);
copy.Remove(without);
return copy;
}
}