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.

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