|
|
- using Networking.Server;
- using System.Collections.Generic;
- using UnityEngine;
-
- [CreateAssetMenu(menuName = "Major Project/Map Generation/Map Manager")]
- public class MapManager : ScriptableObject
- {
- public ClientList clients;
- public GameObject spawn;
- public GameObject spawn4; //The section to use as a spawn-point for games with 2-5 players
- public GameObject spawn8; //The section to use a spawn-point for games with 5-8 players
- //In 5-player games, we choose between them at random
- public List<MapSection> sections; //The list of sections to choose from after starting
- //Split up the inspector lists to make them easier to manage. They'll be combined on initialisation
- public List<SectionList> sectionLists;
-
- public int minConns = 2; //The minimum number of valid connections between two map sections for them to be allowed to link up
-
- public List<MapSection> activeSections; //The list of sections that have been placed on the map (and not removed)
- MapSection lastSection; //Which map-section was most recently added?
- public float startX; //The x-position of the current start of the track
- float startXinit = -16.0f;
- float endX; //The x-position of the current end of the track
- int totalSections; //How many sections have been added? Including ones that have been deleted
-
- int initialPlayerCount;
- int diffStart = 0; //Initial difficulty rating
- int diffCap = 5; //The highest the difficulty rating can go
- int difficulty; //Current difficulty rating
-
- int widthMin; //The minimum widthIn that we want for a new map section
- int widthMax; //The maximum widthIn that we want for a new map section
- int widthMinMin = 3; //The minimum to which widthMin can be reduced
- int widthMaxMin = 5; //The minimum to which widthMax can be reduced
-
- public void init()
- {
- sections = new List<MapSection>();
- startX = startXinit;
- endX = startX;
- totalSections = 0;
-
- foreach (SectionList sectionList in sectionLists)
- {
- foreach (MapSection section in sectionList.sectionList)
- {
- sections.Add(section);
- }
- }
-
- initialPlayerCount = clients.ConnectedClients.Count;
- activeSections = new List<MapSection>();
-
- if (initialPlayerCount < 5)
- {
- addSection(spawn4.GetComponent<MapSection>());
- }
- else if (initialPlayerCount > 5)
- {
- addSection(spawn8.GetComponent<MapSection>());
- }
- else
- {
- if (Random.Range(0.0f, 1.0f) < 0.5f)
- {
- addSection(spawn4.GetComponent<MapSection>());
- }
- else
- {
- addSection(spawn8.GetComponent<MapSection>());
- }
- }
-
- widthMin = activeSections[0].widthOut - 2;
- widthMax = activeSections[0].widthOut + 2;
- difficulty = diffStart;
-
- switch (initialPlayerCount)
- {
- case 2:
- foreach (GameObject spawnBlock in lastSection.spawns2)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- case 3:
- foreach (GameObject spawnBlock in lastSection.spawns3)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- case 4:
- foreach (GameObject spawnBlock in lastSection.spawns4)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- case 5:
- foreach (GameObject spawnBlock in lastSection.spawns5)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- case 6:
- foreach (GameObject spawnBlock in lastSection.spawns6)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- case 7:
- foreach (GameObject spawnBlock in lastSection.spawns7)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- case 8:
- foreach (GameObject spawnBlock in lastSection.spawns8)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- default:
- foreach (GameObject spawnBlock in lastSection.spawns1)
- {
- spawnBlock.GetComponent<Block>().isSpawnable = true;
- }
- break;
- }
- checkForward();
- }
-
- void chooseNextSection()
- {
- //First, we determine which sections are valid
- List<MapSection> validSections = new List<MapSection>();
-
- updateCriteria(); //We update the section selection criteria for the current gamestate
-
- int count = -1;
-
- foreach (MapSection section in sections)
- {
- count++;
- if (section == null)
- {
- continue;
- }
-
- if (section.weight > 0 && checkSegments(section))
- {
- //If a segment is a valid continuation of the current most-recent segment, add it to the list
- //Sections with higher weights get more entries => higher chance of being picked
- for (int i = 0; i < section.weight; i++)
- {
- validSections.Add(section);
- }
- }
- }
- //Having generated our list, we choose a random segment from it
- MapSection nextSection = validSections[(int)Random.Range(0.0f, (float)validSections.Count)];
- addSection(nextSection);
- }
-
- void addSection(MapSection section)
- {
- //Instantiate new section at x = endX
- Vector3 pos = new Vector3(endX, 0.0f, 0.0f);
- GameObject newSection = (GameObject)Instantiate(section.gameObject, pos, Quaternion.identity);
-
- MapSection newSectionScript = newSection.GetComponent<MapSection>();
-
- newSectionScript.InitSection(activeSections.Count);
- newSection.name = newSectionScript.name;
- activeSections.Add(newSectionScript);
- lastSection = newSectionScript;
-
- endX += newSectionScript.length;
- totalSections++;
- }
-
- bool checkSegments(MapSection second)
- {
- return checkSegments(this.lastSection, second);
- }
-
- bool checkSegments(MapSection first, MapSection second)
- {
- int connections = 0;
- //No more than one link section in a row
- if (first.length == 1 && second.length == 1)
- {
- return false;
- }
-
- if (second.difficultyMax < difficulty || second.difficultyMin > difficulty) //Check that we're in the right difficulty range for this section
- {
- return false;
- }
-
- if (second.widthIn < widthMin || second.widthIn > widthMax) //And that it's in the right width range
- {
- return false;
- }
-
- foreach (GameObject exit in first.exits)
- {
- foreach (GameObject entry in second.entrances)
- {
- if (checkConnection(exit, entry))
- {
- connections++;
- }
- }
- }
- return (connections >= minConns);
- }
-
- bool checkConnection(GameObject exit, GameObject entry)
- {
- //If the squares being checked don't line up, the connection is invalid
- if (exit.transform.localPosition.z != entry.transform.localPosition.z)
- {
- return false;
- }
-
- //If both components require jumping (pits or water), the connection is invalid
- //It's technically possible to cross two water blocks, but we don't count that
- if (requiresJump(exit) && requiresJump(entry))
- {
- return false;
- }
-
- //Since we currently don't let people jump over walls, if either block is a wall, the connection is invalid
- if (isWall(exit) || isWall(entry))
- {
- return false;
- }
- //If we've passed all these tests, the connection is valid!
- return true;
- }
-
- bool requiresJump(GameObject block)
- {
- if (block.GetComponent<Block>() == null) //The object must be a pit trap
- {
- return true;
- }
-
- return block.GetComponent<Block>().isWater; //If it's not a pit, then whether it requires jumping depends on whether it's water or not
- }
-
- bool isWall(GameObject block)
- {
- if (block.GetComponent<Block>() == null)
- {
- return true;
- }
-
- return !(block.GetComponent<Block>().is_Walkable);
- }
-
- //Check whether it's time to extend the track forward
- void checkForward()
- {
- //We check if the end of the last section of track is in sight
- Vector3 trackEnd = new Vector3(endX, 0.0f); //Get the middle of the end of the last track section
-
- //If it is, then we add a new section
- if (checkView(trackEnd))
- {
- chooseNextSection();
- checkForward();
- }
- }
-
- //Check whether it's time to delete the oldest section of active track
- void checkBack()
- {
- //We check if the end of the first section of track is still in sight
- Vector3 firstSectionEnd = new Vector3(startX + activeSections[0].length, 0.0f); //Get the middle of the end of the first track section
-
- //If it's not, then we remove it
- if (!(checkView(firstSectionEnd)))
- {
- spawn.GetComponent<blockSpawn>().updatePositions((int)startX + activeSections[0].length);
- startX += activeSections[0].length;
- spawn.GetComponent<blockSpawn>().updatePositions((int)startX);
- activeSections[0].destroySection();
- activeSections.RemoveAt(0);
- }
- }
-
- //Check whether a point is in sight or not
- bool checkView(Vector3 point)
- {
- Vector3 screenPoint = Camera.main.WorldToViewportPoint(point); //Map it into viewport space
-
- //The camera's field of view is represented by 0 > (x, y) < 1, with z being the distance from the camera
- return (screenPoint.z > 0 && screenPoint.x > 0 && screenPoint.x < 1 && screenPoint.y > 0 && screenPoint.y < 1);
- }
-
- //Checks in both directions for sections needing to be added or removed
- public void checkTrack()
- {
- checkForward();
- checkBack();
- }
-
- //Updates minimum and maximum difficulty, width, etc, based on current gamestate
- public void updateCriteria()
- {
- //Start with base values
- difficulty = diffStart;
- //By default, we can add a section 1 tile wider or narrower on either side than the last section
- widthMin = lastSection.widthOut - 2;
- widthMax = lastSection.widthOut + 2;
-
- /* Calculate min & max difficulties & width modifications
- * We recalculate from scratch each time (that is,
- * each time a section is added) so as to avoid having
- * to track which one-off increase has been applied
- * and which hasn't
- */
-
- //As the number of players shrinks, we ramp up the difficulty and contract the track
- if (initialPlayerCount > 0)
- {
- if (clients.ConnectedClients.Count <= (float)(0.5f * initialPlayerCount))
- {
- difficulty++;
- widthMin -= 2;
- widthMax -= 2;
- }
-
- if (clients.ConnectedClients.Count <= (float)(0.33f * initialPlayerCount))
- {
- difficulty += 2;
- widthMin -= 2;
- widthMax -= 2;
- }
- }
- //Ramp up the difficulty as the track extends
- difficulty += ((int)endX - (int)startX) / 7;
-
- //Apply caps
- if (difficulty > diffCap)
- {
- difficulty = diffCap;
- }
-
- if (widthMin < widthMinMin)
- {
- widthMin = widthMinMin;
- }
-
- if (widthMax < widthMaxMin)
- {
- widthMax = widthMaxMin;
- }
- }
-
- // Update is called once per frame
- void Update()
- {
- checkTrack();
- }
- }
|