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.

438 lines
16 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. player.client.Lives--;
  62. }
  63. else
  64. {
  65. survivingPlayers.Add(player);
  66. }
  67. }
  68. }
  69. //If nobody needs a respawn, we stop here. Otherwise, we carry on
  70. if (respawningPlayers.Count > 0)
  71. {
  72. //Next step: figure out the appropriate respawn point
  73. //If there are any players left, it will be the average of their positions
  74. //Otherwise, it will be as close to the centre of the screen as possible
  75. Vector3 respawnLocation = new Vector3(0.0f, -0.5f, 0.0f);
  76. //Debug.Log("Initial respawn location: " + respawnLocation);
  77. if (survivingPlayers.Count > 0) //If any players are not currently waiting to respawn, we get the average of their locations
  78. {
  79. //Debug.Log("Averaging survivor locations");
  80. //We list the positions of all the surviving players
  81. List<Vector3> survivingPlayerLocations = new List<Vector3>();
  82. foreach (PlayerData player in survivingPlayers)
  83. {
  84. survivingPlayerLocations.Add(player.character.transform.position);
  85. }
  86. foreach (Vector3 vec in survivingPlayerLocations)
  87. {
  88. respawnLocation += vec;
  89. }
  90. respawnLocation = respawnLocation / survivingPlayers.Count;
  91. //Debug.Log("Averaged survivor locations, new respawn location: " + respawnLocation);
  92. }
  93. else //Otherwise, we go for the middle of the screen
  94. {
  95. //Debug.Log("Seeking the middle of the screen");
  96. //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
  97. respawnLocation.x = mapManager.startX;
  98. Vector3 respawnLocationVP = Camera.main.WorldToViewportPoint(respawnLocation);
  99. while (respawnLocationVP.x < 0.5f || respawnLocationVP.y < 0.5f)
  100. {
  101. respawnLocation.x += 1.0f;
  102. respawnLocationVP = Camera.main.WorldToViewportPoint(respawnLocation);
  103. //Debug.Log("respawnLocation = " + respawnLocation + ", viewport = " + respawnLocationVP);
  104. }
  105. //Debug.Log("Found middle of camera view, new respawn location: " + respawnLocation);
  106. }
  107. //Now we randomly pick blocks in the vicinity of this point to respawn players on
  108. //Valid blocks are those within x & +/-1 from the chosen location
  109. //If we don't find enough open, unoccupied blocks within that radius, then we widen it
  110. int radius = 0;
  111. List<Block> respawnBlocks;
  112. do
  113. {
  114. respawnBlocks = new List<Block>();
  115. radius++;
  116. //Debug.Log("Finding respawn blocks at radius = " + radius);
  117. for (int i = -1 * radius; i <= radius; i++)
  118. {
  119. for (int j = -1 * radius; j <= radius; j++)
  120. {
  121. Block currentBlock;
  122. Vector3 currentPos = new Vector3(respawnLocation.x + i, respawnLocation.y, respawnLocation.z + j);
  123. //Debug.Log("Checking position " + currentPos);
  124. if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock))
  125. {
  126. /*Debug.Log("Found block at " + currentPos
  127. + ". is_Walkable = " + currentBlock.is_Walkable
  128. + ", currentBlock is Water = " + (currentBlock is Water)
  129. + ", isPit = " + currentBlock.isPit
  130. + ", no currentPlayer = " + (currentBlock.CurrentPlayer == null));*/
  131. if (currentBlock.is_Walkable //Are we allowed on this block?
  132. && !(currentBlock is Water) && !(currentBlock.isPit) //Don't respawn on top of instant traps
  133. && currentBlock.CurrentPlayer == null) //Block must be unoccupied
  134. {
  135. respawnBlocks.Add(currentBlock);
  136. //Debug.Log("Added block at " + currentPos);
  137. }
  138. else
  139. {
  140. //Debug.Log("Block at " + currentPos + " invalid");
  141. }
  142. }
  143. else
  144. {
  145. //Debug.Log("No block at " + currentPos);
  146. }
  147. }
  148. }
  149. } while (respawnBlocks.Count < respawningPlayers.Count);
  150. //Debug.Log("Possible respawn spots: ");
  151. /*foreach(Block block in respawnBlocks)
  152. {
  153. Debug.Log(block.transform.position);
  154. }*/
  155. foreach (PlayerData player in respawningPlayers)
  156. {
  157. //We randomly pick a block for them to respawn on
  158. int respawnIndex = (int)Random.Range(0.0f, (float)respawnBlocks.Count);
  159. //Debug.Log("Respawn index = " + respawnIndex);
  160. Block respawnBlock = respawnBlocks[respawnIndex];
  161. //Debug.Log("Respawn location = " + respawnBlock.transform.position);
  162. player.character.respawnCharacter(respawnBlock);
  163. respawnBlocks.RemoveAt(respawnIndex); //Then we remove it from the list for the next player
  164. //respawnBlocks.Remove(respawnBlock); //Then we remove it from the list for the next player
  165. }
  166. }
  167. }
  168. void cameraCheck(PlayerData[] allPlayers)
  169. {
  170. //Safety check: if anyone has somehow ended up behind the camera before it moved, mark them as needing respawning
  171. checkInView(allPlayers);
  172. //Get the average x-position of all players
  173. float xAvg = 0.0f;
  174. int livePlayerCount = 0;
  175. foreach (PlayerData player in allPlayers)
  176. {
  177. if (!(player.character.respawnNeeded) && player.client.Lives > 0)
  178. {
  179. xAvg += player.character.transform.position.x;
  180. livePlayerCount++;
  181. }
  182. }
  183. if (livePlayerCount > 0)
  184. {
  185. xAvg = xAvg / livePlayerCount;
  186. }
  187. //Turn that position into a vector in viewport space
  188. Vector3 xAvgPos = new Vector3(xAvg, 0.0f, 0.0f);
  189. Vector3 xAvgVP = Camera.main.WorldToViewportPoint(xAvgPos);
  190. //Debug.Log("xAvgPos = " + xAvgPos + ", xAvgVP = " + xAvgVP);
  191. //We move the camera forward by at least one increment
  192. //Keep doing it until the average x-position is roughly centred
  193. do
  194. {
  195. Camera.main.transform.Translate(scrollSpeed, 0, 0, Space.World);
  196. xAvgVP = Camera.main.WorldToViewportPoint(xAvgPos);
  197. //Debug.Log("Camera = " + Camera.main.transform.position + ", xAvgVP = " + xAvgVP);
  198. } while (xAvgVP.x > 0.5f || xAvgVP.y > 0.5f);
  199. //If the foremost player is off the screen, scroll forward to catch up with them
  200. //Find who's furthest ahead
  201. PlayerData playerAhead = allPlayers[0];
  202. foreach (PlayerData player in allPlayers)
  203. {
  204. if (player.client.Lives > 0 && player.character.transform.position.x > playerAhead.character.transform.position.x)
  205. {
  206. playerAhead = player;
  207. }
  208. }
  209. //Turn their position to a viewport vector
  210. Vector3 playerVP = Camera.main.WorldToViewportPoint(playerAhead.character.transform.position);
  211. //If that position is not in view, advance until it is
  212. while (playerVP.x > 1 || playerVP.y > 1)
  213. {
  214. Camera.main.transform.Translate(scrollSpeed, 0, 0, Space.World);
  215. playerVP = Camera.main.WorldToViewportPoint(playerAhead.character.transform.position);
  216. }/**/
  217. //If anyone is out of view at this point, they are dead and need to respawn
  218. checkInView(allPlayers);
  219. /*foreach (PlayerData player in allPlayers)
  220. {
  221. if (player.client.Lives > 0 && !(player.character.respawnNeeded))
  222. {
  223. playerVP = Camera.main.WorldToViewportPoint(player.character.transform.position);
  224. if (playerVP.x < 0.0f || playerVP.y < 0.0f)
  225. {
  226. player.character.respawnNeeded = true;
  227. }
  228. }
  229. }*/
  230. //We check for track sections we need to add/remove
  231. mapManager.checkTrack();
  232. //spawn.wakeup();
  233. //Move the camera forward at a steady rate each round
  234. /*if (scrollSpeed > 0.0f)
  235. {
  236. Camera.main.transform.Translate(scrollSpeed, 0, 0, Space.World);
  237. mapManager.checkTrack();
  238. //Debug.Log("New camera position at x = " + Camera.main.transform.position.x);
  239. //Plan: get average position of surviving players, centre camera view on it
  240. //Also, will need to check every time someone moves: are they still on camera?
  241. //If ahead of camera, advance it until they're visible
  242. //If behind camera, they lose a life & respawn
  243. }
  244. else
  245. {
  246. //Debug.Log("Not scrolling");
  247. }*/
  248. }
  249. //Marks any player out of the camera's view as in need of respawning
  250. void checkInView(PlayerData[] allPlayers)
  251. {
  252. //If anyone is out of view at this point, they are dead and need to respawn
  253. foreach (PlayerData player in allPlayers)
  254. {
  255. if (player.client.Lives > 0 && !(player.character.respawnNeeded))
  256. {
  257. Vector3 playerVP = Camera.main.WorldToViewportPoint(player.character.transform.position);
  258. if (playerVP.x < 0.0f || playerVP.y < 0.0f)
  259. {
  260. player.character.respawnNeeded = true;
  261. }
  262. }
  263. }
  264. }
  265. /// <summary>
  266. /// Checks if the Game is finished
  267. /// </summary>
  268. /// <returns>returns if game is finished</returns>
  269. public override bool isGameOver(PlayerData[] allPlayers)
  270. {
  271. return (RoundCount >= MaxRound -1);
  272. }
  273. /// <summary>
  274. /// Called once per player after they have moved onto a block
  275. /// </summary>
  276. /// <param name="character">Character which moved</param>
  277. /// <param name="client">Client of the character</param>
  278. /// <param name="currentBlock">Block moved onto</param>
  279. protected override void OnPlayerMoved(Character character, ClientData client, Block currentBlock)
  280. {
  281. handleFalling(character, client, currentBlock, character.justMoved);
  282. }
  283. protected override void OnRoundStart(PlayerData[] allPlayers)
  284. {
  285. }
  286. protected override void OnAllPlayersMoved(PlayerData[] allPlayers)
  287. {
  288. foreach (PlayerData player in allPlayers)
  289. {
  290. /* The justMoved variable is used to determine whether a player taking their turn in the water should become stuck
  291. * (because they moved into/in the water), or not (because they're turning while remaining in the same square)
  292. * It's not needed from move to move, so we clear it
  293. */
  294. player.character.justMoved = false;
  295. }
  296. }
  297. protected override void OnGameOver(PlayerData[] allPlayers)
  298. {
  299. throw new System.NotImplementedException();
  300. }
  301. private bool isOwned(Block block, out ClientData client)
  302. {
  303. client = null;
  304. foreach (KeyValuePair<ClientData, List<Block>> ownedList in BlocksOwned)
  305. {
  306. if (ownedList.Value.Contains(block))
  307. {
  308. client = ownedList.Key;
  309. return true;
  310. }
  311. }
  312. return false;
  313. }
  314. private void handleFalling(Character character, ClientData client, Block currentBlock, bool didMove)
  315. {
  316. //If a character has fallen in the water or into a pit, we mark that fact, and they lose the rest of their turn
  317. character.inWater = currentBlock.isWater;
  318. character.respawnNeeded = currentBlock.isPit;
  319. character.onCrystal = currentBlock.isCrystals;
  320. character.underRock = currentBlock.isRock;
  321. if (didMove && (character.inWater || character.respawnNeeded))
  322. {
  323. character.stuck = true;
  324. }
  325. }
  326. protected override void OnPlayerKilled(Character character, ClientData client)
  327. {
  328. if (character.respawnNeeded || character.onCrystal)
  329. {
  330. character.lives -= 1;
  331. character.ClientLink.Lives = character.lives;
  332. }
  333. if(character.underRock && currentBoulderCount < 1)
  334. {
  335. character.lives -= 1;
  336. character.ClientLink.Lives = character.lives;
  337. }
  338. }
  339. private IEnumerator AnimateBlock(Material mat, float time)
  340. {
  341. float timeElasped = 0;
  342. while (timeElasped < time)
  343. {
  344. mat.SetFloat("_Multiplier", (timeElasped / time));
  345. yield return new WaitForEndOfFrame();
  346. timeElasped += Time.deltaTime;
  347. }
  348. mat.SetFloat("_Multiplier", 1);
  349. }
  350. protected override void OnGameStart(PlayerData[] AllPlayers)
  351. {
  352. BlocksOwned = new Dictionary<ClientData, List<Block>>();
  353. RoundCount = 0;
  354. AllPlayers = getPlayerOrder(AllPlayers);
  355. for (int i = 0; i < AllPlayers.Length; i++)
  356. {
  357. OnPlayerMoved(AllPlayers[i].character, AllPlayers[i].client, AllPlayers[i].character.CurrentBlock);
  358. }
  359. }
  360. public override PlayerData[] getPlayerOrder(PlayerData[] AllPlayers)
  361. {
  362. AllPlayers = AllPlayers.ToArray().OrderBy(unit => unit.character.CurrentBlock.transform.position.x).ToArray();
  363. int order = 1;
  364. foreach(PlayerData data in AllPlayers)
  365. {
  366. data.character.runOrder = order;
  367. order++;
  368. }
  369. return AllPlayers;
  370. }
  371. }