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.

453 lines
16 KiB

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