1
Fork 0
slime-blood-and-pain/Assets/Scripts/Map.cs

408 lines
14 KiB
C#
Raw Normal View History

2019-04-27 11:38:29 +00:00
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
2019-04-27 14:15:38 +00:00
[System.Serializable]
public class ImpossibleCorridorError : System.Exception
{
public ImpossibleCorridorError() { }
public ImpossibleCorridorError(string message) : base(message) { }
public ImpossibleCorridorError(string message, System.Exception inner) : base(message, inner) { }
protected ImpossibleCorridorError(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
public class MapRoom {
2019-04-27 15:20:25 +00:00
public readonly Vector2Int start;
public readonly Vector2Int end;
2019-04-27 14:15:38 +00:00
public readonly int mapSize;
2019-04-27 17:04:27 +00:00
public MapRoom(int mapSize, int maxRoomSize, int minRoomSize) {
2019-04-27 14:15:38 +00:00
this.mapSize = mapSize;
2019-04-27 17:04:27 +00:00
start = new Vector2Int(Random.Range(1, mapSize-1), Random.Range(1, mapSize-1));
end = new Vector2Int(Random.Range(1, mapSize-1), Random.Range(1, mapSize-1));
2019-04-27 14:15:38 +00:00
if(start.x > end.x) {
int swap = start.x;
start.x = end.x;
end.x = swap;
}
if(start.y > end.y) {
int swap = start.y;
start.y = end.y;
end.y = swap;
}
2019-04-27 15:18:09 +00:00
while(end.x - start.x > maxRoomSize) {
2019-04-27 14:48:37 +00:00
end.x--;
start.x++;
}
2019-04-27 15:18:09 +00:00
while(end.y - start.y > maxRoomSize) {
2019-04-27 14:48:37 +00:00
end.y--;
start.y++;
}
2019-04-27 17:04:27 +00:00
while(end.x - start.x < minRoomSize) {
end.x++;
start.x--;
}
while(end.y - start.y < minRoomSize) {
end.y++;
start.y--;
}
start.Clamp(new Vector2Int(1, 1), new Vector2Int(mapSize-1, mapSize-1));
end.Clamp(new Vector2Int(1, 1), new Vector2Int(mapSize-1, mapSize-1));
2019-04-27 14:15:38 +00:00
}
2019-04-27 17:19:37 +00:00
public Vector2Int RandomPoint() {
return new Vector2Int(Random.Range(start.x, end.x+1), Random.Range(start.y, end.y+1));
2019-04-27 14:15:38 +00:00
}
}
public class MapCorridor {
2019-04-27 15:20:25 +00:00
public readonly Vector2Int start;
public readonly Vector2Int end;
2019-04-27 14:15:38 +00:00
public readonly bool horizontal_priority;
public MapCorridor(MapRoom from, MapRoom to, int mapSize) {
2019-04-27 17:19:37 +00:00
start = from.RandomPoint();
end = to.RandomPoint();
2019-04-27 14:15:38 +00:00
//50%
horizontal_priority = Random.Range(0f, 1f) >= 0.5f;
}
}
2019-04-27 11:38:29 +00:00
public class Map : MonoBehaviour
{
2019-04-27 12:21:33 +00:00
[BeforeStartAttribute]
public int mapSize = 30;
[BeforeStartAttribute]
public int roomsToGenerate = 5;
2019-04-27 17:04:27 +00:00
[BeforeStartAttribute]
public int minRoomSize = 2;
2019-04-27 14:48:37 +00:00
[BeforeStartAttribute]
public int maxRoomSize = 6;
2019-04-27 15:18:09 +00:00
[BeforeStartAttribute]
public int maxRoomIterations = 100;
2019-04-27 12:21:33 +00:00
[BeforeStartAttribute]
2019-04-27 23:34:30 +00:00
public List<Sprite> floorSprites;
2019-04-27 14:15:38 +00:00
2019-04-27 23:34:30 +00:00
[BeforeStartAttribute]
public List<Sprite> botWallSprites;
[BeforeStartAttribute]
2019-04-29 17:53:45 +00:00
public GameObject tilePrefab;
2019-04-27 23:34:30 +00:00
[BeforeStartAttribute]
2019-04-29 17:53:45 +00:00
public GameObject playerPrefab;
2019-04-27 23:34:30 +00:00
[BeforeStartAttribute]
2019-04-29 17:53:45 +00:00
public List<GameObject> enemyPrefabs;
2019-04-27 14:15:38 +00:00
[BeforeStartAttribute]
2019-04-29 17:53:45 +00:00
public int enemiesToSpawn = 10;
2019-04-27 11:38:29 +00:00
2019-04-27 20:19:35 +00:00
[BeforeStartAttribute]
2019-04-29 17:53:45 +00:00
public List<GameObject> curiositiesPrefabs;
2019-04-27 20:19:35 +00:00
[BeforeStartAttribute]
2019-04-29 17:53:45 +00:00
public int curiositiesToSpawn = 35;
2019-04-27 20:19:35 +00:00
[BeforeStartAttribute]
2019-04-29 17:53:45 +00:00
public GameObject stairsPrefab;
2019-04-27 20:19:35 +00:00
2019-04-29 20:44:58 +00:00
[BeforeStartAttribute]
public GameObject shopCorePrefab;
[BeforeStartAttribute]
public GameObject shopEdgePrefab;
[BeforeStartAttribute]
public GameObject[] shopItems; //3
2019-04-27 14:15:38 +00:00
private GameObject[,] tiles;
private List<MapRoom> rooms;
2019-04-27 20:19:35 +00:00
private TurnHandler turnHandler;
2019-04-27 14:15:38 +00:00
2019-04-28 14:34:50 +00:00
public MapTile GetTile(Vector2Int position) {
2019-04-27 23:34:30 +00:00
try {
GameObject tileObject = tiles[position.x, position.y];
2019-04-28 14:34:50 +00:00
return tileObject.GetComponent<MapTile>();
2019-04-27 23:34:30 +00:00
}
catch(System.IndexOutOfRangeException) {
return null;
}
2019-04-27 11:38:29 +00:00
}
2019-04-27 20:19:35 +00:00
public bool CanMoveTo(Vector2Int position)
2019-04-27 15:57:01 +00:00
{
2019-04-27 17:04:27 +00:00
try {
2019-04-29 18:47:35 +00:00
MapTile tile = GetTile(position);
if(tile == null) return false;
bool walkable = tile.walkable;
2019-04-28 14:28:31 +00:00
List<Entity> entities = turnHandler.GetEntitiesAtPosition(position);
2019-04-28 12:11:21 +00:00
bool free = true;
foreach(Entity entity in entities) {
free &= entity.overlappable;
}
2019-04-27 20:19:35 +00:00
return walkable && free;
2019-04-27 17:04:27 +00:00
}
2019-04-27 20:19:35 +00:00
catch(System.IndexOutOfRangeException) {
2019-04-27 17:04:27 +00:00
return false;
}
2019-04-27 15:57:01 +00:00
}
2019-04-27 23:34:30 +00:00
private void EditTile(Vector2Int position, bool walkable, bool roomPart) {
2019-04-28 14:34:50 +00:00
MapTile tile = GetTile(position);
2019-04-27 14:15:38 +00:00
tile.walkable = walkable;
2019-04-27 18:13:28 +00:00
tile.roomPart |= roomPart;
2019-04-27 11:38:29 +00:00
}
private void FillWithWalls() {
2019-04-27 12:21:33 +00:00
for(int x = 0; x < mapSize; x++) {
for(int y = 0; y < mapSize; y++) {
GameObject tileObject = Instantiate(tilePrefab, transform);
tileObject.transform.position = new Vector3(x, y, 0);
tiles[x, y] = tileObject;
2019-04-27 15:18:09 +00:00
tileObject.name = "Tile [" + x.ToString() + ", " + y.ToString() + "]";
2019-04-28 14:34:50 +00:00
MapTile tile = tileObject.GetComponent<MapTile>();
2019-04-27 12:21:33 +00:00
tile.walkable = false;
2019-04-27 11:38:29 +00:00
}
}
}
2019-04-27 14:15:38 +00:00
private void PlaceRoom(MapRoom mr) {
for(int x = mr.start.x; x <= mr.end.x; x++) {
for(int y = mr.start.y; y <= mr.end.y; y++) {
2019-04-27 23:34:30 +00:00
EditTile(new Vector2Int(x, y), true, true);
2019-04-27 11:38:29 +00:00
}
}
}
2019-04-27 14:15:38 +00:00
private bool ScanRoom(MapRoom mr) {
//Returns true if the room can be safely placed
for(int x = Mathf.Clamp(mr.start.x-1, 0, mapSize-1); x <= Mathf.Clamp(mr.end.x+1, 0, mapSize-1); x++) {
for(int y = Mathf.Clamp(mr.start.y-1, 0, mapSize-1); y <= Mathf.Clamp(mr.end.y+1, 0, mapSize-1); y++) {
2019-04-27 15:28:38 +00:00
if(GetTile(new Vector2Int(x, y)).roomPart) {
2019-04-27 14:15:38 +00:00
return false;
}
2019-04-27 11:38:29 +00:00
}
}
2019-04-27 14:15:38 +00:00
return true;
}
private void PlaceCorridor(MapCorridor mc) {
2019-04-27 15:20:25 +00:00
Vector2Int cursor = new Vector2Int(mc.start.x, mc.start.y);
2019-04-27 23:34:30 +00:00
EditTile(cursor, true, false);
2019-04-27 14:15:38 +00:00
if(mc.horizontal_priority) {
while(cursor.x != mc.end.x) {
if(cursor.x > mc.end.x) cursor.x--;
else cursor.x++;
2019-04-27 23:34:30 +00:00
EditTile(cursor, true, false);
2019-04-27 14:15:38 +00:00
}
while(cursor.y != mc.end.y) {
if(cursor.y > mc.end.y) cursor.y--;
else cursor.y++;
2019-04-27 23:34:30 +00:00
EditTile(cursor, true, false);
2019-04-27 11:38:29 +00:00
}
2019-04-27 14:15:38 +00:00
}
else
{
while(cursor.y != mc.end.y) {
if(cursor.y > mc.end.y) cursor.y--;
else cursor.y++;
2019-04-27 23:34:30 +00:00
EditTile(cursor, true, false);
2019-04-27 14:15:38 +00:00
}
while(cursor.x != mc.end.x) {
if(cursor.x > mc.end.x) cursor.x--;
else cursor.x++;
2019-04-27 23:34:30 +00:00
EditTile(cursor, true, false);
2019-04-27 11:38:29 +00:00
}
}
}
private void GenerateMap() {
FillWithWalls();
2019-04-27 15:18:09 +00:00
int roomIterations = 0;
while(rooms.Count < roomsToGenerate && roomIterations < maxRoomIterations) {
roomIterations++;
2019-04-27 17:04:27 +00:00
MapRoom room = new MapRoom(mapSize, maxRoomSize, minRoomSize);
2019-04-27 14:15:38 +00:00
if(ScanRoom(room)) {
2019-04-27 17:04:27 +00:00
//Fill with the room
2019-04-27 14:15:38 +00:00
PlaceRoom(room);
rooms.Add(room);
}
2019-04-27 17:04:27 +00:00
//Place a corridor
2019-04-27 14:15:38 +00:00
if(rooms.Count > 1) {
2019-04-27 15:18:09 +00:00
MapRoom from = rooms[rooms.Count-2];
MapRoom to = rooms[rooms.Count-1];
2019-04-27 14:48:37 +00:00
try {
MapCorridor corridor = new MapCorridor(from, to, mapSize);
PlaceCorridor(corridor);
}
2019-04-27 17:04:27 +00:00
catch (ImpossibleCorridorError) {
}
2019-04-27 14:15:38 +00:00
}
2019-04-27 12:21:33 +00:00
}
2019-04-27 11:38:29 +00:00
}
2019-04-27 20:19:35 +00:00
private void PlacePlayer() {
2019-04-29 08:39:48 +00:00
//Check for an existing player
2019-04-27 20:19:35 +00:00
MapRoom room = rooms[Random.Range(0, rooms.Count)];
Vector2Int point = room.RandomPoint();
2019-04-29 08:39:48 +00:00
GameObject playerObject = GameObject.FindGameObjectWithTag("Player");
if(playerObject == null) {
playerObject = Instantiate(playerPrefab, turnHandler.transform);
}
else {
playerObject.transform.parent = turnHandler.transform;
}
2019-04-27 20:19:35 +00:00
playerObject.name = "Player";
playerObject.transform.position = new Vector3(point.x, point.y, 0);
}
private void PlaceEnemies() {
for(int i = 0; i < enemiesToSpawn; i++) {
MapRoom room = rooms[Random.Range(0, rooms.Count)];
2019-04-29 20:44:58 +00:00
Vector2Int point;
while(true) {
point = room.RandomPoint();
if(turnHandler.GetEntitiesAtPosition(point).Count > 0) {
continue;
}
break;
2019-04-29 18:47:35 +00:00
}
2019-04-27 20:19:35 +00:00
GameObject enemyPrefab = enemyPrefabs[Random.Range(0, enemyPrefabs.Count)];
GameObject enemyObject = Instantiate(enemyPrefab, turnHandler.transform);
enemyObject.name = "Enemy " + i.ToString();
enemyObject.transform.position = new Vector3(point.x, point.y, 0);
}
}
2019-04-29 17:53:45 +00:00
private void PlaceCuriosities() {
for(int i = 0; i < curiositiesToSpawn; i++) {
MapRoom room = rooms[Random.Range(0, rooms.Count)];
2019-04-29 20:44:58 +00:00
Vector2Int point;
while(true) {
point = room.RandomPoint();
if(turnHandler.GetEntitiesAtPosition(point).Count > 0) {
continue;
}
break;
2019-04-29 18:47:35 +00:00
}
2019-04-29 17:53:45 +00:00
GameObject curiosityPrefab = curiositiesPrefabs[Random.Range(0, curiositiesPrefabs.Count)];
GameObject curiosityObject = Instantiate(curiosityPrefab, turnHandler.transform);
curiosityObject.name = "Curiosity " + i.ToString();
curiosityObject.transform.position = new Vector3(point.x, point.y, 0);
}
}
private void PlaceStairs() {
MapRoom room = rooms[Random.Range(0, rooms.Count)];
2019-04-29 20:44:58 +00:00
Vector2Int point;
while(true) {
point = room.RandomPoint();
if(turnHandler.GetEntitiesAtPosition(point).Count > 0) {
continue;
}
break;
}
2019-04-29 17:53:45 +00:00
GameObject curiosityObject = Instantiate(stairsPrefab, turnHandler.transform);
curiosityObject.name = "Stairs";
curiosityObject.transform.position = new Vector3(point.x, point.y, 0);
}
2019-04-27 23:34:30 +00:00
public static Sprite SampleSprite(List<Sprite> list) {
return list[Random.Range(0, list.Count)];
}
public void GenerateTileSprites() {
for(int x = 0; x < mapSize; x++) {
for(int y = 0; y < mapSize; y++) {
2019-04-28 14:34:50 +00:00
MapTile tile = GetTile(new Vector2Int(x, y));
MapTile otherTile;
2019-04-27 23:34:30 +00:00
if(tile.walkable) tile.sprite = SampleSprite(floorSprites);
else if((bool)(otherTile = GetTile(new Vector2Int(x, y+1))) && otherTile.walkable) tile.sprite = SampleSprite(botWallSprites);
//TODO: corners
}
}
2019-04-27 17:04:27 +00:00
}
2019-04-29 20:44:58 +00:00
private void PlaceShop() {
MapRoom room = rooms[Random.Range(0, rooms.Count)];
Vector2Int point;
while(true) {
bool done = true;
point = room.RandomPoint();
for(int x = -2; x <= 2; x++) {
for(int y = -2; y <= 2; y++) {
if(turnHandler.GetEntitiesAtPosition(new Vector2Int(x, y)).Count > 0) {
done = false;
}
if(!done) break;
}
if(!done) break;
}
if(done) break;
}
GameObject shopCore = Instantiate(shopCorePrefab, turnHandler.transform);
shopCore.name = "Shop (Core)";
shopCore.transform.position = new Vector3(point.x, point.y, 0);
GameObject shopEdge = Instantiate(shopEdgePrefab, turnHandler.transform);
shopEdge.name = "Shop (Edge)";
shopEdge.transform.position = new Vector3(point.x+1, point.y, 0);
shopEdge = Instantiate(shopEdgePrefab, turnHandler.transform);
shopEdge.name = "Shop (Edge)";
shopEdge.transform.position = new Vector3(point.x-1, point.y, 0);
shopEdge = Instantiate(shopEdgePrefab, turnHandler.transform);
shopEdge.name = "Shop (Edge)";
shopEdge.transform.position = new Vector3(point.x+1, point.y+1, 0);
shopEdge = Instantiate(shopEdgePrefab, turnHandler.transform);
shopEdge.name = "Shop (Edge)";
shopEdge.transform.position = new Vector3(point.x-1, point.y+1, 0);
shopEdge = Instantiate(shopEdgePrefab, turnHandler.transform);
shopEdge.name = "Shop (Edge)";
shopEdge.transform.position = new Vector3(point.x, point.y+1, 0);
shopEdge = Instantiate(shopEdgePrefab, turnHandler.transform);
shopEdge.name = "Shop (Edge)";
GameObject shopItem1 = Instantiate(shopItems[0], turnHandler.transform);
shopItem1.transform.position = new Vector3(point.x, point.y-1, 0);
shopEdge.name = "Shop Item 1";
GameObject shopItem2 = Instantiate(shopItems[1], turnHandler.transform);
shopItem2.transform.position = new Vector3(point.x+1, point.y-1, 0);
shopEdge.name = "Shop Item 2";
GameObject shopItem3 = Instantiate(shopItems[2], turnHandler.transform);
shopItem3.transform.position = new Vector3(point.x-1, point.y-1, 0);
shopEdge.name = "Shop Item 3";
}
2019-04-29 13:27:26 +00:00
public void NewLevel() {
//Cleanup everything.
transform.parent.BroadcastMessage("OnNewLevel");
2019-04-27 14:15:38 +00:00
//Initialize everything
2019-04-27 12:21:33 +00:00
tiles = new GameObject[mapSize, mapSize];
2019-04-27 14:15:38 +00:00
rooms = new List<MapRoom>();
2019-04-27 20:19:35 +00:00
turnHandler = GameObject.FindGameObjectWithTag("GameController").GetComponentInChildren<TurnHandler>();
2019-04-27 23:34:30 +00:00
GenerateMap();
2019-04-29 20:44:58 +00:00
GenerateTileSprites();
PlaceShop();
2019-04-27 20:19:35 +00:00
PlacePlayer();
PlaceEnemies();
2019-04-29 17:53:45 +00:00
PlaceCuriosities();
PlaceStairs();
}
public void OnNewLevel() {
//Suppress the error
2019-04-27 11:38:29 +00:00
}
2019-04-29 13:27:26 +00:00
private void Start()
{
NewLevel();
}
2019-04-27 11:38:29 +00:00
}