|
|
|
using System;
|
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Linq;
|
|
|
|
using Unity.Netcode;
|
|
|
|
using Unity.VisualScripting;
|
|
|
|
using UnityEngine;
|
|
|
|
using Object = UnityEngine.Object;
|
|
|
|
using Random = UnityEngine.Random;
|
|
|
|
|
|
|
|
namespace Game {
|
|
|
|
public class GameManager : NetworkBehaviour {
|
|
|
|
|
|
|
|
public BalanceValues balanceValues;
|
|
|
|
|
|
|
|
public Object ballPrefab;
|
|
|
|
public Object playerPrefab;
|
|
|
|
public Object borderZonePrefab;
|
|
|
|
public Object modificationPrefab;
|
|
|
|
public ModificationProperties[] modifications;
|
|
|
|
public Object wormholePrefab;
|
|
|
|
public Object newBallPrefab;
|
|
|
|
|
|
|
|
public static GameManager Singleton { get; private set; }
|
|
|
|
|
|
|
|
public static BalanceValues BalanceValues => Singleton.balanceValues;
|
|
|
|
|
|
|
|
public List<Ball> Balls { get; } = new();
|
|
|
|
|
|
|
|
public Player Player1 { get; private set; }
|
|
|
|
|
|
|
|
public Player Player2 { get; private set; }
|
|
|
|
|
|
|
|
private void Awake() {
|
|
|
|
Singleton = this;
|
|
|
|
|
|
|
|
SetupPlayers();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void OnEnable() {
|
|
|
|
// Move this to settings
|
|
|
|
Application.targetFrameRate = 144;
|
|
|
|
QualitySettings.vSyncCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private IEnumerator SpawnLoop(Spawnable spawnable) {
|
|
|
|
SpawnRate required = RoomSettings.SpawnRates[spawnable];
|
|
|
|
if (required == SpawnRate.Never)
|
|
|
|
yield break;
|
|
|
|
float baseSeconds = balanceValues.spawnRates.First(s => s.spawnable == spawnable).values.First(v => v.spawnRate == required).baseSeconds;
|
|
|
|
while (Application.isPlaying) {
|
|
|
|
yield return new WaitForSeconds(baseSeconds);
|
|
|
|
switch (spawnable) {
|
|
|
|
case Spawnable.NewBallTemporary:
|
|
|
|
SpawnNewBall(false);
|
|
|
|
break;
|
|
|
|
case Spawnable.NewBallPermanent:
|
|
|
|
SpawnNewBall(true);
|
|
|
|
break;
|
|
|
|
case Spawnable.Modification:
|
|
|
|
SpawnModification();
|
|
|
|
break;
|
|
|
|
case Spawnable.Wormhole:
|
|
|
|
SpawnWormhole();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Start() {
|
|
|
|
SpawnBall(Player1, true);
|
|
|
|
StartCoroutine(SpawnLoop(Spawnable.NewBallTemporary));
|
|
|
|
StartCoroutine(SpawnLoop(Spawnable.NewBallPermanent));
|
|
|
|
StartCoroutine(SpawnLoop(Spawnable.Modification));
|
|
|
|
StartCoroutine(SpawnLoop(Spawnable.Wormhole));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SetupPlayers() {
|
|
|
|
var p1Obj = Instantiate(playerPrefab);
|
|
|
|
var p2Obj = Instantiate(playerPrefab);
|
|
|
|
|
|
|
|
Player p1, p2;
|
|
|
|
switch (RoomSettings.Type) {
|
|
|
|
case Type.AI:
|
|
|
|
p1 = p1Obj.AddComponent<AIPlayer>();
|
|
|
|
p2 = p2Obj.AddComponent<AIPlayer>();
|
|
|
|
((AIPlayer) p1).Difficulty = RoomSettings.AIDifficulty;
|
|
|
|
((AIPlayer) p2).Difficulty = RoomSettings.AIDifficulty;
|
|
|
|
break;
|
|
|
|
case Type.RealOnline:
|
|
|
|
p1 = p1Obj.AddComponent<RealPlayer>();
|
|
|
|
p2 = p2Obj.AddComponent<RealPlayer>();
|
|
|
|
((RealPlayer) p1).isThisClient = true;
|
|
|
|
break;
|
|
|
|
case Type.RealOffline:
|
|
|
|
p1 = p1Obj.AddComponent<RealPlayer>();
|
|
|
|
p2 = p2Obj.AddComponent<RealPlayer>();
|
|
|
|
((RealPlayer) p1).isThisClient = true;
|
|
|
|
((RealPlayer) p2).isThisClient = true;
|
|
|
|
break;
|
|
|
|
case Type.Hybrid:
|
|
|
|
p1 = p1Obj.AddComponent<RealPlayer>();
|
|
|
|
p2 = p2Obj.AddComponent<AIPlayer>();
|
|
|
|
((RealPlayer) p1).isThisClient = true;
|
|
|
|
((AIPlayer) p2).Difficulty = RoomSettings.AIDifficulty;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
}
|
|
|
|
|
|
|
|
p1.Side = Side.Bottom;
|
|
|
|
p2.Side = Side.Top;
|
|
|
|
|
|
|
|
Player1 = p1;
|
|
|
|
Player2 = p2;
|
|
|
|
|
|
|
|
p1.borderZonePrefab = p2.borderZonePrefab = borderZonePrefab;
|
|
|
|
}
|
|
|
|
|
|
|
|
private Vector2 GetSpawnPosition() {
|
|
|
|
Vector2 area = balanceValues.spawnArea * Dimensions.Singleton.playGroundSize * 0.5f;
|
|
|
|
float x = Random.Range(-area.x, area.x);
|
|
|
|
float y = Random.Range(-area.y, area.y);
|
|
|
|
return new Vector2(x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SpawnNewBall(bool permanent) {
|
|
|
|
Instantiate(newBallPrefab, GetSpawnPosition(), Quaternion.identity).GetComponent<NewBall>().IsPermanent = permanent;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SpawnWormhole() {
|
|
|
|
Instantiate(wormholePrefab, GetSpawnPosition(), Quaternion.identity);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void SpawnModification() {
|
|
|
|
var properties = modifications[Random.Range(0, modifications.Length)];
|
|
|
|
var mod = Instantiate(modificationPrefab, GetSpawnPosition(), Quaternion.identity).GetComponent<Modification>();
|
|
|
|
mod.Properties = properties;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SpawnBall(Player towards, bool isPermanent) {
|
|
|
|
float startSpeed = balanceValues.ballSpeed;
|
|
|
|
var position = new Vector2(0, -towards.transform.position.y * 0.5f);
|
|
|
|
var ball = Instantiate(ballPrefab, position, Quaternion.identity).GetComponent<Ball>();
|
|
|
|
ball.Rb.velocity = RandomDirectionTowards(towards) * startSpeed;
|
|
|
|
ball.IsPermanent = isPermanent;
|
|
|
|
ball.Radius = 0.5f;
|
|
|
|
|
|
|
|
Balls.Add(ball);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Vector2 RandomDirectionTowards(Player player) {
|
|
|
|
const float maxAngle = 45;
|
|
|
|
var radians = Random.Range(-maxAngle, maxAngle) * Mathf.PI / 180;
|
|
|
|
var x = Mathf.Sin(radians);
|
|
|
|
var y = Mathf.Cos(radians) * player.Side switch {
|
|
|
|
Side.Top => 1,
|
|
|
|
Side.Bottom => -1,
|
|
|
|
_ => throw new ArgumentOutOfRangeException()
|
|
|
|
};
|
|
|
|
return new Vector2(x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void RemoveBall(Ball ball) {
|
|
|
|
Balls.Remove(ball);
|
|
|
|
Destroy(ball.gameObject);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|