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.

435 lines
15 KiB

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using Networking.Server;
  5. using TMPro;
  6. using UnityEngine.UI;
  7. using System.Linq;
  8. [CreateAssetMenu(menuName = "Major Project/GameModes/Racetrack", order = 201)]
  9. public class RacetrackGameMode : GameMode
  10. {
  11. public MapManager mapManager;
  12. public blockSpawn spawn;
  13. public int MaxRound = 999;
  14. public string nextScene = "ServerTestScene";
  15. List<ClientData> ConnectedClients;
  16. public Material OverlayMaterial;
  17. public float scrollSpeed = 1.0f; //The rate at which the level will scroll past
  18. [SerializeField]
  19. [Tooltip("Layers to ignore when checking for blocks")]
  20. public LayerMask Ignore;
  21. public int RoundCount { get; private set; }
  22. private Dictionary<ClientData, List<Block>> BlocksOwned;
  23. int currentBoulderCount;
  24. /// <summary>
  25. /// Called once before any players have spawned
  26. /// </summary>
  27. protected override void OnPreGameStart()
  28. {
  29. mapManager.init();
  30. }
  31. /// <summary>
  32. /// Called once all players have finished their moves but before the Objective is checked
  33. /// </summary>
  34. protected override void OnRoundEnd(PlayerData[] allPlayers)
  35. {
  36. cameraCheck(allPlayers);
  37. //At the end of each round, any stuck players are freed to resume moving next round
  38. respawnPlayers(allPlayers);
  39. /*foreach (PlayerData player in allPlayers)
  40. {
  41. player.character.stuck = false;
  42. if (player.character.respawnNeeded && player.client.Lives > 0)
  43. {
  44. player.character.respawnCharacter();
  45. }
  46. }*/
  47. RoundCount++;
  48. }
  49. void respawnPlayers(PlayerData[] allPlayers)
  50. {
  51. //First step: determine if anyone is waiting to respawn
  52. List<PlayerData> respawningPlayers = new List<PlayerData>();
  53. List<PlayerData> survivingPlayers = new List<PlayerData>();
  54. foreach (PlayerData player in allPlayers)
  55. {
  56. if (player.client.Lives > 0)
  57. {
  58. if (player.character.respawnNeeded)
  59. {
  60. respawningPlayers.Add(player);
  61. }
  62. else
  63. {
  64. survivingPlayers.Add(player);
  65. }
  66. }
  67. }
  68. //If nobody needs a respawn, we stop here. Otherwise, we carry on
  69. if (respawningPlayers.Count > 0)
  70. {
  71. //Next step: figure out the appropriate respawn point
  72. //If there are any players left, it will be the average of their positions
  73. //Otherwise, it will be as close to the centre of the screen as possible
  74. Vector3 respawnLocation = new Vector3(0.0f, -0.5f, 0.0f);
  75. if (survivingPlayers.Count > 0) //If any players are not currently waiting to respawn, we get the average of their locations
  76. {
  77. //We list the positions of all the surviving players
  78. List<Vector3> survivingPlayerLocations = new List<Vector3>();
  79. foreach (PlayerData player in survivingPlayers)
  80. {
  81. survivingPlayerLocations.Add(player.character.transform.position);
  82. }
  83. foreach (Vector3 vec in survivingPlayerLocations)
  84. {
  85. respawnLocation += vec;
  86. }
  87. respawnLocation = respawnLocation / survivingPlayers.Count;
  88. }
  89. else //Otherwise, we go for the middle of the screen
  90. {
  91. //We start in the middle of the track, at the back, and scroll forward until we're past the centre of the camera's view
  92. respawnLocation.x = mapManager.startX;
  93. Vector3 respawnLocationVP = Camera.main.WorldToViewportPoint(respawnLocation);
  94. while (respawnLocationVP.x > 0.5f || respawnLocationVP.y > 0.5f)
  95. {
  96. respawnLocation.x += 1.0f;
  97. respawnLocationVP = Camera.main.WorldToViewportPoint(respawnLocation);
  98. }
  99. }
  100. //Now we randomly pick blocks in the vicinity of this point to respawn players on
  101. //Valid blocks are those within x & +/-1 from the chosen location
  102. //If we don't find enough open, unoccupied blocks within that radius, then we widen it
  103. int radius = 0;
  104. List<Block> respawnBlocks;
  105. do
  106. {
  107. respawnBlocks = new List<Block>();
  108. radius++;
  109. for (int i = -1 * radius; i <= radius; i++)
  110. {
  111. for (int j = -1 * radius; j <= radius; j++)
  112. {
  113. Block currentBlock;
  114. Vector3 currentPos = new Vector3(respawnLocation.x + i, respawnLocation.y, respawnLocation.z);
  115. if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock) //Does a block exist here?
  116. && currentBlock.is_Walkable //Are we allowed on this block?
  117. && !(currentBlock.isWater) && !(currentBlock.isPit) //Don't respawn on top of instant traps
  118. && currentBlock.CurrentPlayer == null) //Block must be unoccupied
  119. {
  120. respawnBlocks.Add(currentBlock);
  121. }
  122. }
  123. }
  124. } while (respawnBlocks.Count < respawningPlayers.Count);
  125. foreach (PlayerData player in respawningPlayers)
  126. {
  127. //We randomly pick a block for them to respawn on
  128. Block respawnBlock = respawnBlocks[(int)Random.Range(0.0f, (float)respawnBlocks.Count)];
  129. Vector3 respawnLocationPlayer = respawnBlock.transform.position;
  130. player.character.respawnCharacter(respawnLocationPlayer);
  131. respawnBlocks.Remove(respawnBlock); //Then we remove it from the list for the next player
  132. }
  133. }
  134. }
  135. void cameraCheck(PlayerData[] allPlayers)
  136. {
  137. //Get the average x-position of all players
  138. float xAvg = 0.0f;
  139. int livePlayerCount = 0;
  140. foreach (PlayerData player in allPlayers)
  141. {
  142. if (!(player.character.respawnNeeded) && player.client.Lives > 0)
  143. {
  144. xAvg += player.character.transform.position.x;
  145. livePlayerCount++;
  146. }
  147. }
  148. if (livePlayerCount > 0)
  149. {
  150. xAvg = xAvg / livePlayerCount;
  151. }
  152. //Turn that position into a vector in viewport space
  153. Vector3 xAvgPos = new Vector3(xAvg, 0.0f, 0.0f);
  154. Vector3 xAvgVP = Camera.main.WorldToViewportPoint(xAvgPos);
  155. //Debug.Log("xAvgPos = " + xAvgPos + ", xAvgVP = " + xAvgVP);
  156. //We move the camera forward by at least one increment
  157. //Keep doing it until the average x-position is roughly centred
  158. do
  159. {
  160. Camera.main.transform.Translate(scrollSpeed, 0, 0, Space.World);
  161. xAvgVP = Camera.main.WorldToViewportPoint(xAvgPos);
  162. //Debug.Log("Camera = " + Camera.main.transform.position + ", xAvgVP = " + xAvgVP);
  163. } while (xAvgVP.x > 0.5f || xAvgVP.y > 0.5f);
  164. //If the foremost player is off the screen, scroll forward to catch up with them
  165. //Find who's furthest ahead
  166. PlayerData playerAhead = allPlayers[0];
  167. foreach (PlayerData player in allPlayers)
  168. {
  169. if (player.client.Lives > 0 && player.character.transform.position.x > playerAhead.character.transform.position.x)
  170. {
  171. playerAhead = player;
  172. }
  173. }
  174. //Turn their position to a viewport vector
  175. Vector3 playerVP = Camera.main.WorldToViewportPoint(playerAhead.character.transform.position);
  176. //If that position is not in view, advance until it is
  177. while (playerVP.x > 1 || playerVP.y > 1)
  178. {
  179. Camera.main.transform.Translate(scrollSpeed, 0, 0, Space.World);
  180. playerVP = Camera.main.WorldToViewportPoint(playerAhead.character.transform.position);
  181. }/**/
  182. //If anyone is out of view at this point, they are dead and need to respawn
  183. foreach (PlayerData player in allPlayers)
  184. {
  185. if (player.client.Lives > 0 && !(player.character.respawnNeeded))
  186. {
  187. playerVP = Camera.main.WorldToViewportPoint(player.character.transform.position);
  188. if (playerVP.x < 0.0f || playerVP.y < 0.0f)
  189. {
  190. player.character.respawnNeeded = true;
  191. }
  192. }
  193. }
  194. //We check for track sections we need to add/remove
  195. mapManager.checkTrack();
  196. //spawn.wakeup();
  197. //Move the camera forward at a steady rate each round
  198. /*if (scrollSpeed > 0.0f)
  199. {
  200. Camera.main.transform.Translate(scrollSpeed, 0, 0, Space.World);
  201. mapManager.checkTrack();
  202. //Debug.Log("New camera position at x = " + Camera.main.transform.position.x);
  203. //Plan: get average position of surviving players, centre camera view on it
  204. //Also, will need to check every time someone moves: are they still on camera?
  205. //If ahead of camera, advance it until they're visible
  206. //If behind camera, they lose a life & respawn
  207. }
  208. else
  209. {
  210. //Debug.Log("Not scrolling");
  211. }*/
  212. }
  213. /// <summary>
  214. /// Checks if the Game is finished
  215. /// </summary>
  216. /// <returns>returns if game is finished</returns>
  217. public override bool isGameOver(PlayerData[] allPlayers)
  218. {
  219. return (RoundCount >= MaxRound -1);
  220. }
  221. /// <summary>
  222. /// Called once per player after they have moved onto a block
  223. /// </summary>
  224. /// <param name="character">Character which moved</param>
  225. /// <param name="client">Client of the character</param>
  226. /// <param name="currentBlock">Block moved onto</param>
  227. protected override void OnPlayerMoved(Character character, ClientData client, Block currentBlock)
  228. {
  229. handleFalling(character, client, currentBlock, character.justMoved);
  230. /*Debug.Log("Moved to square at " + currentBlock.transform.position.x + ", "
  231. + currentBlock.transform.position.y + ", "
  232. + currentBlock.transform.position.z);
  233. //If a character has fallen in the water or into a pit, we mark that fact, and they lose the rest of their turn
  234. character.inWater = currentBlock.isWater;
  235. character.respawnNeeded = currentBlock.isPit;
  236. if (character.inWater == true || character.respawnNeeded == true)
  237. {
  238. character.stuck = true;
  239. }
  240. Debug.Log("inWater = " + character.inWater + ", respawnNeeded = " + character.respawnNeeded + ", stuck = " + character.stuck);*/
  241. //Commented out because we don't do this in the racetrack mode
  242. /*ClientData OwnedClient;
  243. Material overlay = null;
  244. if (isOwned(currentBlock, out OwnedClient))
  245. {
  246. if (OwnedClient == client)
  247. return;
  248. BlocksOwned[OwnedClient].Remove(currentBlock);
  249. foreach (Material mat in currentBlock.GetComponent<Renderer>().materials)
  250. {
  251. if (mat.name == OverlayMaterial.name + " (Instance)")
  252. overlay = mat;
  253. }
  254. }
  255. if (overlay == null)
  256. {
  257. overlay = new Material(OverlayMaterial);
  258. List<Material> mats = new List<Material>(currentBlock.GetComponent<Renderer>().materials);
  259. mats.Add(overlay);
  260. currentBlock.GetComponent<Renderer>().materials = mats.ToArray();
  261. }
  262. overlay.SetColor("_NewColor", client.Color);
  263. if (!BlocksOwned.ContainsKey(client))
  264. BlocksOwned.Add(client, new List<Block>());
  265. BlocksOwned[client].Add(currentBlock);
  266. if (overlay != null)
  267. currentBlock.StartCoroutine(AnimateBlock(overlay, 0.25f));*/
  268. }
  269. protected override void OnRoundStart(PlayerData[] allPlayers)
  270. {
  271. }
  272. protected override void OnAllPlayersMoved(PlayerData[] allPlayers)
  273. {
  274. foreach (PlayerData player in allPlayers)
  275. {
  276. /* The justMoved variable is used to determine whether a player taking their turn in the water should become stuck
  277. * (because they moved into/in the water), or not (because they're turning while remaining in the same square)
  278. * It's not needed from move to move, so we clear it
  279. */
  280. player.character.justMoved = false;
  281. /* if (BlocksOwned.ContainsKey(player.client))
  282. player.client.SceneScore = BlocksOwned[player.client].Count;
  283. else
  284. player.client.SceneScore = 0;*/
  285. }
  286. }
  287. protected override void OnGameOver(PlayerData[] allPlayers)
  288. {
  289. throw new System.NotImplementedException();
  290. }
  291. private bool isOwned(Block block, out ClientData client)
  292. {
  293. client = null;
  294. foreach (KeyValuePair<ClientData, List<Block>> ownedList in BlocksOwned)
  295. {
  296. if (ownedList.Value.Contains(block))
  297. {
  298. client = ownedList.Key;
  299. return true;
  300. }
  301. }
  302. return false;
  303. }
  304. private void handleFalling(Character character, ClientData client, Block currentBlock, bool didMove)
  305. {
  306. //If a character has fallen in the water or into a pit, we mark that fact, and they lose the rest of their turn
  307. character.inWater = currentBlock.isWater;
  308. character.respawnNeeded = currentBlock.isPit;
  309. character.onCrystal = currentBlock.isCrystals;
  310. character.underRock = currentBlock.isRock;
  311. if (didMove && (character.inWater || character.respawnNeeded))
  312. {
  313. character.stuck = true;
  314. }
  315. //Debug.Log("inWater = " + character.inWater + ", respawnNeeded = " + character.respawnNeeded + ", stuck = " + character.stuck);
  316. }
  317. protected override void OnPlayerKilled(Character character, ClientData client)
  318. {
  319. if (character.respawnNeeded || character.onCrystal)
  320. {
  321. character.lives -= 1;
  322. character.ClientLink.Lives = character.lives;
  323. }
  324. if(character.underRock && currentBoulderCount < 1)
  325. {
  326. character.lives -= 1;
  327. character.ClientLink.Lives = character.lives;
  328. }
  329. }
  330. private IEnumerator AnimateBlock(Material mat, float time)
  331. {
  332. float timeElasped = 0;
  333. while (timeElasped < time)
  334. {
  335. mat.SetFloat("_Multiplier", (timeElasped / time));
  336. yield return new WaitForEndOfFrame();
  337. timeElasped += Time.deltaTime;
  338. }
  339. mat.SetFloat("_Multiplier", 1);
  340. }
  341. protected override void OnGameStart(PlayerData[] AllPlayers)
  342. {
  343. BlocksOwned = new Dictionary<ClientData, List<Block>>();
  344. RoundCount = 0;
  345. AllPlayers = getPlayerOrder(AllPlayers);
  346. for (int i = 0; i < AllPlayers.Length; i++)
  347. {
  348. OnPlayerMoved(AllPlayers[i].character, AllPlayers[i].client, AllPlayers[i].character.CurrentBlock);
  349. }
  350. }
  351. public override PlayerData[] getPlayerOrder(PlayerData[] AllPlayers)
  352. {
  353. AllPlayers = AllPlayers.ToArray().OrderBy(unit => unit.character.CurrentBlock.transform.position.x).ToArray();
  354. int order = 1;
  355. foreach(PlayerData data in AllPlayers)
  356. {
  357. data.character.runOrder = order;
  358. order++;
  359. }
  360. return AllPlayers;
  361. }
  362. }