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.

383 lines
13 KiB

  1. using Networking.Server;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. [CreateAssetMenu(menuName = "Major Project/Map Generation/Map Manager")]
  5. public class MapManager : ScriptableObject
  6. {
  7. public ClientList clients;
  8. public GameObject spawn;
  9. public GameObject spawn4; //The section to use as a spawn-point for games with 2-5 players
  10. public GameObject spawn8; //The section to use a spawn-point for games with 5-8 players
  11. //In 5-player games, we choose between them at random
  12. public List<MapSection> sections; //The list of sections to choose from after starting
  13. //Split up the inspector lists to make them easier to manage. They'll be combined on initialisation
  14. public List<SectionList> sectionLists;
  15. public int minConns = 2; //The minimum number of valid connections between two map sections for them to be allowed to link up
  16. public List<MapSection> activeSections; //The list of sections that have been placed on the map (and not removed)
  17. MapSection lastSection; //Which map-section was most recently added?
  18. public float startX; //The x-position of the current start of the track
  19. float startXinit = -16.0f;
  20. float endX; //The x-position of the current end of the track
  21. int totalSections; //How many sections have been added? Including ones that have been deleted
  22. int initialPlayerCount;
  23. int diffStart = 0; //Initial difficulty rating
  24. int diffCap = 5; //The highest the difficulty rating can go
  25. int difficulty; //Current difficulty rating
  26. int widthMin; //The minimum widthIn that we want for a new map section
  27. int widthMax; //The maximum widthIn that we want for a new map section
  28. int widthMinMin = 3; //The minimum to which widthMin can be reduced
  29. int widthMaxMin = 5; //The minimum to which widthMax can be reduced
  30. public void init()
  31. {
  32. sections = new List<MapSection>();
  33. startX = startXinit;
  34. endX = startX;
  35. totalSections = 0;
  36. foreach (SectionList sectionList in sectionLists)
  37. {
  38. foreach (MapSection section in sectionList.sectionList)
  39. {
  40. sections.Add(section);
  41. }
  42. }
  43. initialPlayerCount = clients.ConnectedClients.Count;
  44. activeSections = new List<MapSection>();
  45. if (initialPlayerCount < 5)
  46. {
  47. addSection(spawn4.GetComponent<MapSection>());
  48. }
  49. else if (initialPlayerCount > 5)
  50. {
  51. addSection(spawn8.GetComponent<MapSection>());
  52. }
  53. else
  54. {
  55. if (Random.Range(0.0f, 1.0f) < 0.5f)
  56. {
  57. addSection(spawn4.GetComponent<MapSection>());
  58. }
  59. else
  60. {
  61. addSection(spawn8.GetComponent<MapSection>());
  62. }
  63. }
  64. widthMin = activeSections[0].widthOut - 2;
  65. widthMax = activeSections[0].widthOut + 2;
  66. difficulty = diffStart;
  67. switch (initialPlayerCount)
  68. {
  69. case 2:
  70. foreach (GameObject spawnBlock in lastSection.spawns2)
  71. {
  72. spawnBlock.GetComponent<Block>().isSpawnable = true;
  73. }
  74. break;
  75. case 3:
  76. foreach (GameObject spawnBlock in lastSection.spawns3)
  77. {
  78. spawnBlock.GetComponent<Block>().isSpawnable = true;
  79. }
  80. break;
  81. case 4:
  82. foreach (GameObject spawnBlock in lastSection.spawns4)
  83. {
  84. spawnBlock.GetComponent<Block>().isSpawnable = true;
  85. }
  86. break;
  87. case 5:
  88. foreach (GameObject spawnBlock in lastSection.spawns5)
  89. {
  90. spawnBlock.GetComponent<Block>().isSpawnable = true;
  91. }
  92. break;
  93. case 6:
  94. foreach (GameObject spawnBlock in lastSection.spawns6)
  95. {
  96. spawnBlock.GetComponent<Block>().isSpawnable = true;
  97. }
  98. break;
  99. case 7:
  100. foreach (GameObject spawnBlock in lastSection.spawns7)
  101. {
  102. spawnBlock.GetComponent<Block>().isSpawnable = true;
  103. }
  104. break;
  105. case 8:
  106. foreach (GameObject spawnBlock in lastSection.spawns8)
  107. {
  108. spawnBlock.GetComponent<Block>().isSpawnable = true;
  109. }
  110. break;
  111. default:
  112. foreach (GameObject spawnBlock in lastSection.spawns1)
  113. {
  114. spawnBlock.GetComponent<Block>().isSpawnable = true;
  115. }
  116. break;
  117. }
  118. checkForward();
  119. }
  120. void chooseNextSection()
  121. {
  122. Debug.Log("<b>Choosing next section </b>");
  123. //First, we determine which sections are valid
  124. List<MapSection> validSections = new List<MapSection>();
  125. updateCriteria(); //We update the section selection criteria for the current gamestate
  126. int count = -1;
  127. foreach (MapSection section in sections)
  128. {
  129. count++;
  130. if (section == null)
  131. {
  132. continue;
  133. }
  134. Debug.Log("Checking section " + section.name + ", weight = " + section.weight);
  135. if (section.weight > 0 && checkSegments(section))
  136. {
  137. //If a segment is a valid continuation of the current most-recent segment, add it to the list
  138. //Sections with higher weights get more entries => higher chance of being picked
  139. for (int i = 0; i < section.weight; i++)
  140. {
  141. validSections.Add(section);
  142. }
  143. }
  144. }
  145. //Having generated our list, we choose a random segment from it
  146. int selectedIndex = Random.Range(0, validSections.Count);
  147. Debug.Log("<b>Validmap sections: </b>" + validSections.Count);
  148. Debug.Log("<b>Selected section: </b>" + selectedIndex);
  149. MapSection nextSection = validSections[selectedIndex];
  150. addSection(nextSection);
  151. }
  152. void addSection(MapSection section)
  153. {
  154. //Instantiate new section at x = endX
  155. Vector3 pos = new Vector3(endX, 0.0f, 0.0f);
  156. GameObject newSection = (GameObject)Instantiate(section.gameObject, pos, Quaternion.identity);
  157. MapSection newSectionScript = newSection.GetComponent<MapSection>();
  158. newSectionScript.InitSection(activeSections.Count);
  159. newSection.name = newSectionScript.name;
  160. activeSections.Add(newSectionScript);
  161. lastSection = newSectionScript;
  162. endX += newSectionScript.length;
  163. totalSections++;
  164. }
  165. bool checkSegments(MapSection second)
  166. {
  167. return checkSegments(this.lastSection, second);
  168. }
  169. bool checkSegments(MapSection first, MapSection second)
  170. {
  171. Debug.Log("Checking " + first.name + " & " + second.name + ". DiffMin = " + second.difficultyMin + ", DiffMax = " + second.difficultyMax);
  172. int connections = 0;
  173. //No more than one link section in a row
  174. if (first.length == 1 && second.length == 1)
  175. {
  176. Debug.Log("Disqualified: repeated link sections");
  177. return false;
  178. }
  179. if (second.difficultyMax < difficulty || second.difficultyMin > difficulty) //Check that we're in the right difficulty range for this section
  180. {
  181. Debug.Log("Disqualified: wrong difficulty");
  182. return false;
  183. }
  184. if (second.widthIn < widthMin || second.widthIn > widthMax) //And that it's in the right width range
  185. {
  186. Debug.Log("Disqualified: wrong width");
  187. return false;
  188. }
  189. foreach (GameObject exit in first.exits)
  190. {
  191. foreach (GameObject entry in second.entrances)
  192. {
  193. if (checkConnection(exit, entry))
  194. {
  195. connections++;
  196. }
  197. }
  198. }
  199. Debug.Log("Check result: " + (connections >= minConns));
  200. return (connections >= minConns);
  201. }
  202. bool checkConnection(GameObject exit, GameObject entry)
  203. {
  204. //If the squares being checked don't line up, the connection is invalid
  205. if (exit.transform.localPosition.z != entry.transform.localPosition.z)
  206. {
  207. return false;
  208. }
  209. //If both components require jumping (pits or water), the connection is invalid
  210. //It's technically possible to cross two water blocks, but we don't count that
  211. if (requiresJump(exit) && requiresJump(entry))
  212. {
  213. return false;
  214. }
  215. //Since we currently don't let people jump over walls, if either block is a wall, the connection is invalid
  216. if (isWall(exit) || isWall(entry))
  217. {
  218. return false;
  219. }
  220. //If we've passed all these tests, the connection is valid!
  221. return true;
  222. }
  223. bool requiresJump(GameObject block)
  224. {
  225. if (block.GetComponent<Block>() == null) //The object must be a pit trap
  226. {
  227. return true;
  228. }
  229. return block.GetComponent<Block>().isWater; //If it's not a pit, then whether it requires jumping depends on whether it's water or not
  230. }
  231. bool isWall(GameObject block)
  232. {
  233. if (block.GetComponent<Block>() == null)
  234. {
  235. return true;
  236. }
  237. return !(block.GetComponent<Block>().is_Walkable);
  238. }
  239. //Check whether it's time to extend the track forward
  240. void checkForward()
  241. {
  242. //We check if the end of the last section of track is in sight
  243. Vector3 trackEnd = new Vector3(endX, 0.0f); //Get the middle of the end of the last track section
  244. //If it is, then we add a new section
  245. if (checkView(trackEnd))
  246. {
  247. chooseNextSection();
  248. checkForward();
  249. }
  250. }
  251. //Check whether it's time to delete the oldest section of active track
  252. void checkBack()
  253. {
  254. //We check if the end of the first section of track is still in sight
  255. Vector3 firstSectionEnd = new Vector3(startX + activeSections[0].length, 0.0f); //Get the middle of the end of the first track section
  256. //If it's not, then we remove it
  257. if (!(checkView(firstSectionEnd)))
  258. {
  259. spawn.GetComponent<blockSpawn>().updatePositions((int)startX + activeSections[0].length);
  260. startX += activeSections[0].length;
  261. spawn.GetComponent<blockSpawn>().updatePositions((int)startX);
  262. activeSections[0].destroySection();
  263. activeSections.RemoveAt(0);
  264. }
  265. }
  266. //Check whether a point is in sight or not
  267. bool checkView(Vector3 point)
  268. {
  269. Vector3 screenPoint = Camera.main.WorldToViewportPoint(point); //Map it into viewport space
  270. //The camera's field of view is represented by 0 > (x, y) < 1, with z being the distance from the camera
  271. return (screenPoint.z > 0 && screenPoint.x > 0 && screenPoint.x < 1 && screenPoint.y > 0 && screenPoint.y < 1);
  272. }
  273. //Checks in both directions for sections needing to be added or removed
  274. public void checkTrack()
  275. {
  276. checkForward();
  277. checkBack();
  278. }
  279. //Updates minimum and maximum difficulty, width, etc, based on current gamestate
  280. public void updateCriteria()
  281. {
  282. //Start with base values
  283. difficulty = diffStart;
  284. //By default, we can add a section 1 tile wider or narrower on either side than the last section
  285. widthMin = lastSection.widthOut - 2;
  286. widthMax = lastSection.widthOut + 2;
  287. /* Calculate min & max difficulties & width modifications
  288. * We recalculate from scratch each time (that is,
  289. * each time a section is added) so as to avoid having
  290. * to track which one-off increase has been applied
  291. * and which hasn't
  292. */
  293. //As the number of players shrinks, we ramp up the difficulty and contract the track
  294. if (initialPlayerCount > 0)
  295. {
  296. if (clients.ConnectedClients.Count <= (float)(0.5f * initialPlayerCount))
  297. {
  298. difficulty++;
  299. widthMin -= 2;
  300. widthMax -= 2;
  301. }
  302. if (clients.ConnectedClients.Count <= (float)(0.33f * initialPlayerCount))
  303. {
  304. difficulty += 2;
  305. widthMin -= 2;
  306. widthMax -= 2;
  307. }
  308. }
  309. //Ramp up the difficulty as the track extends
  310. difficulty += ((int)endX - (int)startX) / 7;
  311. //Apply caps
  312. if (difficulty > diffCap)
  313. {
  314. difficulty = diffCap;
  315. }
  316. if (widthMin < widthMinMin)
  317. {
  318. widthMin = widthMinMin;
  319. }
  320. if (widthMax < widthMaxMin)
  321. {
  322. widthMax = widthMaxMin;
  323. }
  324. Debug.Log("Difficulty = " + difficulty + ", widthMin = " + widthMin + ", widthMax = " + widthMax);
  325. }
  326. // Update is called once per frame
  327. void Update()
  328. {
  329. checkTrack();
  330. }
  331. }