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.

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