720 lines
21 KiB
C#
720 lines
21 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Fusion;
|
|
using Fusion.Addons.InterestManagement;
|
|
|
|
namespace TPSBR
|
|
{
|
|
public struct SpawnData
|
|
{
|
|
public NetworkBehaviour Prefab;
|
|
public Vector3 Position;
|
|
public Quaternion Rotation;
|
|
|
|
public bool IsConnector;
|
|
public int AreaID;
|
|
public Material Material;
|
|
public float Height;
|
|
|
|
public SpawnData(NetworkBehaviour prefab, Vector3 position, Quaternion rotation)
|
|
{
|
|
Prefab = prefab;
|
|
Position = position;
|
|
Rotation = rotation;
|
|
|
|
IsConnector = false;
|
|
AreaID = -1;
|
|
Material = null;
|
|
Height = 0f;
|
|
}
|
|
}
|
|
|
|
public interface IBlockConnector
|
|
{
|
|
public bool IsHalfSide { get; }
|
|
public float MinHeight { get; }
|
|
public float MaxHeight { get; }
|
|
public bool NeedsNetworkSpawn { get; }
|
|
|
|
public void SetMaterial(int areaID, Material material);
|
|
public void SetHeight(float height);
|
|
}
|
|
|
|
public class LevelGenerator : CoreBehaviour
|
|
{
|
|
// PUBLIC MEMBERS
|
|
|
|
public AreaSetup[] Areas => _areas;
|
|
|
|
public List<SpawnData> ObjectsToSpawn => _objectsToSpawn;
|
|
public List<SpawnData> ItemBoxesToSpawn => _itemBoxesToSpawn;
|
|
public Vector2Int Dimensions => Vector2Int.one * _size * _blockSize;
|
|
public Vector3 Center => new Vector3(_size * _blockSize * 0.5f - _blockSize * 0.5f, 0f, _size * _blockSize * 0.5f - _blockSize * 0.5f);
|
|
public int BlockSize => _blockSize;
|
|
|
|
// PRIVATE MEMBERS
|
|
|
|
[Header("Surface")]
|
|
[SerializeField]
|
|
private int _blockSize = 26;
|
|
[SerializeField]
|
|
private int _maxHeight = 30;
|
|
[SerializeField]
|
|
private float _noiseScale = 0.9f;
|
|
[SerializeField]
|
|
private BlockSpawn _areaBlockSpawn;
|
|
[SerializeField]
|
|
private BlockSpawn _flatlandBlockSpawn;
|
|
[SerializeField]
|
|
private BlockSpawn _coastBlockSpawn;
|
|
[SerializeField]
|
|
private Transform _water;
|
|
|
|
[Header("Areas")]
|
|
[SerializeField]
|
|
private AreaSetup[] _areas;
|
|
[SerializeField, Tooltip("How much of the total space will be occupied by Areas if not overlapping")]
|
|
private float _areaOccupancy = 0.5f;
|
|
[SerializeField, Range(0f, 1f)]
|
|
private float _randomizeAreaSize = 0.2f;
|
|
[SerializeField, Range(0f, 1f)]
|
|
private float _randomizeAreaHeight = 0.2f;
|
|
[SerializeField]
|
|
private Vector2 _flatlandInfluence = new Vector2(0f, 0.1f);
|
|
[SerializeField]
|
|
private Vector2 _coastInfluence = new Vector2(-0.03f, -0.01f);
|
|
|
|
[Header("Connectors")]
|
|
[SerializeField]
|
|
private GameObject[] _connectorPrefabs;
|
|
|
|
[Header("Object Spawn")]
|
|
[SerializeField]
|
|
private ItemBox _itemBoxPrefab;
|
|
[SerializeField]
|
|
private Vector2 _areaSpawnChance = new Vector2(0.1f, 0.8f);
|
|
[SerializeField]
|
|
private float _flatlandSpawnChance = 0.01f;
|
|
|
|
private IBlockConnector[][] _sortedConnectorPrefabs;
|
|
|
|
private BlockData[,] _blocks;
|
|
private List<SpawnData> _objectsToSpawn = new List<SpawnData>(1024);
|
|
private List<SpawnData> _itemBoxesToSpawn = new List<SpawnData>(1024);
|
|
|
|
private Vector3[] _rotations = { new Vector3(0f, 0f, 0f), new Vector3(0f, 90f, 0f), new Vector3(0f, 180f, 0f), new Vector3(0f, 270f, 0f)};
|
|
|
|
private int _size;
|
|
private int _areaCount;
|
|
|
|
// PUBLIC METHODS
|
|
|
|
public void Generate(int seed, int size, int areaCount)
|
|
{
|
|
_size = size;
|
|
_areaCount = areaCount;
|
|
|
|
Random.InitState(seed);
|
|
|
|
_blocks = new BlockData[size, size];
|
|
|
|
GenerateAreas();
|
|
GenerateAreaConnections();
|
|
SpawnBlocks();
|
|
|
|
SortConnectorPrefabs();
|
|
SpawnConnectors();
|
|
|
|
_water.position = new Vector3(Center.x, _water.position.y, Center.z);
|
|
_water.localScale = new Vector3(Dimensions.x, 1f, Dimensions.y);
|
|
}
|
|
|
|
// PRIVATE METHODS
|
|
|
|
private LevelBlock GenerateRandomBlock(Vector3 position, BlockSpawn blockSpawn)
|
|
{
|
|
return GenerateBlock(blockSpawn.GetBlockPrefab(), position);
|
|
}
|
|
|
|
private LevelBlock GenerateBlock(LevelBlock blockPrefab, Vector3 position)
|
|
{
|
|
var rotation = Quaternion.Euler(_rotations[Random.Range(0, _rotations.Length)]);
|
|
var block = Instantiate(blockPrefab, position, rotation, transform);
|
|
|
|
if (block.AllowMirror == true && Random.value > 0.5f)
|
|
{
|
|
block.transform.localScale = new Vector3(-1f, 1f, 1f);
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
private void GenerateAreas()
|
|
{
|
|
float areaSize = (_size * _size * _areaOccupancy * _blockSize * _blockSize) / _areaCount;
|
|
float areaRadius = Mathf.Sqrt(areaSize / Mathf.PI);
|
|
|
|
float areaRadiusMin = Mathf.Max(_blockSize, areaRadius - _randomizeAreaSize * areaRadius);
|
|
float areaRadiusMax = areaRadius + _randomizeAreaSize * areaRadius;
|
|
|
|
for (int areaID = 0; areaID < _areaCount; areaID++)
|
|
{
|
|
var area = _areas[areaID];
|
|
area.Radius = Random.Range(areaRadiusMin, areaRadiusMax);
|
|
area.MaxHeight = Random.Range(Mathf.Max(1f, _maxHeight - _randomizeAreaHeight * _maxHeight), _maxHeight + _randomizeAreaHeight * _maxHeight);
|
|
|
|
Debug.Log($"Area {areaID}, Radius {area.Radius}, Max Height {area.MaxHeight}");
|
|
|
|
// Aditional radius for flatland(white) blocks
|
|
float flatlandRadius = area.Radius > _blockSize * 3f ? area.Radius + _blockSize * 2f : area.Radius + _blockSize;
|
|
|
|
int landRadiusBlocks = Mathf.RoundToInt(flatlandRadius / _blockSize);
|
|
|
|
int centerAreaStart = Mathf.Min(landRadiusBlocks, Mathf.RoundToInt(_size * 0.5f - 1f));
|
|
int centerAreaEnd = Mathf.Max(_size - landRadiusBlocks - 1, Mathf.RoundToInt(_size * 0.5f));
|
|
|
|
area.Center = GetRandomAreaCenter(new Vector2Int(centerAreaStart, centerAreaStart), new Vector2Int(centerAreaEnd, centerAreaEnd));
|
|
|
|
var start = area.Center - new Vector2Int(landRadiusBlocks, landRadiusBlocks);
|
|
start.x = Mathf.Max(0, start.x);
|
|
start.y = Mathf.Max(0, start.y);
|
|
|
|
var end = area.Center + new Vector2Int(landRadiusBlocks, landRadiusBlocks);
|
|
end.x = Mathf.Min(_size - 1, end.x);
|
|
end.y = Mathf.Min(_size - 1, end.y);
|
|
|
|
Vector2 areaCenter = area.Center;
|
|
|
|
float sqrRadius = area.Radius * area.Radius;
|
|
float sqrFlatlandRadius = flatlandRadius * flatlandRadius;
|
|
|
|
for (int x = start.x; x <= end.x; x++)
|
|
{
|
|
for (int z = start.y; z <= end.y; z++)
|
|
{
|
|
var blockPosition = new Vector2(x, z);
|
|
var direction = (blockPosition - areaCenter) * _blockSize;
|
|
|
|
float sqrDistance = direction.sqrMagnitude;
|
|
|
|
if (sqrDistance > sqrFlatlandRadius)
|
|
continue;
|
|
|
|
var block = _blocks[x, z];
|
|
|
|
if (sqrDistance > sqrRadius)
|
|
{
|
|
block.IsFlatland = true;
|
|
_blocks[x, z] = block;
|
|
|
|
continue;
|
|
}
|
|
|
|
float distance = direction.magnitude;
|
|
float influence = 1f - distance / area.Radius;
|
|
|
|
if (influence > block.AreaInfluence)
|
|
{
|
|
block.AreaID = areaID;
|
|
block.AreaInfluence = influence;
|
|
}
|
|
else
|
|
{
|
|
block.IsFlatland = true;
|
|
}
|
|
|
|
_blocks[x, z] = block;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GenerateAreaConnections()
|
|
{
|
|
for (int areaID = 0; areaID < _areaCount; areaID++)
|
|
{
|
|
var areaCenter = _areas[areaID].Center;
|
|
|
|
int targetAreaID = (areaID + 1) % _areaCount;
|
|
var targetAreaCenter = _areas[targetAreaID].Center;
|
|
|
|
var currentPosition = areaCenter;
|
|
|
|
while (currentPosition != targetAreaCenter)
|
|
{
|
|
if (currentPosition.x != targetAreaCenter.x)
|
|
{
|
|
currentPosition.x += currentPosition.x < targetAreaCenter.x ? 1 : -1;
|
|
}
|
|
|
|
if (currentPosition.y != targetAreaCenter.y)
|
|
{
|
|
currentPosition.y += currentPosition.y < targetAreaCenter.y ? 1 : -1;
|
|
}
|
|
|
|
var block = _blocks[currentPosition.x, currentPosition.y];
|
|
|
|
if (block.AreaID == targetAreaID)
|
|
break; // Next area reached
|
|
|
|
if (block.AreaInfluence <= 0f && block.IsFlatland == false)
|
|
{
|
|
block.IsFlatland = true;
|
|
|
|
_blocks[currentPosition.x, currentPosition.y] = block;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SpawnBlocks()
|
|
{
|
|
var boxSpawnPositions = ListPool.Get<Transform>(128);
|
|
|
|
for (int x = 0; x < _size; x++)
|
|
{
|
|
for (int z = 0; z < _size; z++)
|
|
{
|
|
var block = _blocks[x, z];
|
|
|
|
bool spawnBoxes = false;
|
|
|
|
if (block.AreaInfluence > 0.999f) // Area center
|
|
{
|
|
float yNormalized = Mathf.PerlinNoise(x * _noiseScale, z * _noiseScale);
|
|
float yPosition = Mathf.Round(yNormalized * _areas[block.AreaID].MaxHeight * block.AreaInfluence);
|
|
|
|
block.Block = GenerateBlock(_areas[block.AreaID].CenterBlock, new Vector3(x * _blockSize, yPosition, z * _blockSize));
|
|
spawnBoxes = true;
|
|
}
|
|
else if (block.AreaInfluence > 0f) // Area
|
|
{
|
|
float yNormalized = Mathf.PerlinNoise(x * _noiseScale, z * _noiseScale);
|
|
float yPosition = Mathf.Round(yNormalized * _areas[block.AreaID].MaxHeight * block.AreaInfluence);
|
|
|
|
block.Block = GenerateRandomBlock(new Vector3(x * _blockSize, yPosition, z * _blockSize), _areaBlockSpawn);
|
|
|
|
float itemBoxChance = Mathf.Lerp(_areaSpawnChance.x, _areaSpawnChance.y, block.AreaInfluence);
|
|
spawnBoxes = block.Block.AlwaysSpawnBoxes == true || Random.value < itemBoxChance;
|
|
}
|
|
else if (block.IsFlatland == true)
|
|
{
|
|
float yNormalized = Mathf.PerlinNoise(x * _noiseScale, z * _noiseScale);
|
|
float yPosition = Mathf.Round(yNormalized * _maxHeight * Random.Range(_flatlandInfluence.x, _flatlandInfluence.y));
|
|
|
|
block.Block = GenerateRandomBlock(new Vector3(x * _blockSize, yPosition, z * _blockSize), _flatlandBlockSpawn);
|
|
|
|
spawnBoxes = block.Block.AlwaysSpawnBoxes == true || Random.value < _flatlandSpawnChance;
|
|
}
|
|
else if (IsCoastBlock(x, z) == true)
|
|
{
|
|
float yNormalized = Mathf.PerlinNoise(x * _noiseScale, z * _noiseScale);
|
|
float yPosition = yNormalized * _maxHeight * Random.Range(_coastInfluence.x, _coastInfluence.y);
|
|
|
|
block.Block = GenerateRandomBlock(new Vector3(x * _blockSize, yPosition, z * _blockSize), _coastBlockSpawn);
|
|
block.Block.EnableSpawnPoints(false);
|
|
|
|
spawnBoxes = block.Block.AlwaysSpawnBoxes;
|
|
}
|
|
|
|
if (block.AreaID >= 0)
|
|
{
|
|
block.Block.SetMaterial(_areas[block.AreaID].Material);
|
|
}
|
|
|
|
if (spawnBoxes == true)
|
|
{
|
|
boxSpawnPositions.Clear();
|
|
int spawnCount = block.Block.GetRandomItemBoxPositions(boxSpawnPositions);
|
|
|
|
for (int i = 0; i < spawnCount; i++)
|
|
{
|
|
var boxPosition = boxSpawnPositions[i];
|
|
_itemBoxesToSpawn.Add(new SpawnData(_itemBoxPrefab, boxPosition.position, boxPosition.rotation));
|
|
}
|
|
}
|
|
|
|
if (block.Block != null && block.Block.GetInterestProvider(out Transform interestProviderSpawnPoint, out InterestProvider interestProviderPrefab) == true)
|
|
{
|
|
_objectsToSpawn.Add(new SpawnData(interestProviderPrefab, interestProviderSpawnPoint.position, interestProviderSpawnPoint.rotation));
|
|
}
|
|
|
|
_blocks[x, z] = block;
|
|
}
|
|
}
|
|
|
|
ListPool.Return(boxSpawnPositions);
|
|
}
|
|
|
|
private void SortConnectorPrefabs()
|
|
{
|
|
if (_sortedConnectorPrefabs != null)
|
|
return;
|
|
|
|
int maxConnectors = _connectorPrefabs.Length;
|
|
|
|
var connectorPrefabs = ListPool.Get<IBlockConnector>(maxConnectors);
|
|
var connectors = ListPool.Get<IBlockConnector>(maxConnectors);
|
|
|
|
for (int i = 0; i < maxConnectors; i++)
|
|
{
|
|
connectorPrefabs.Add(_connectorPrefabs[i].GetComponent<IBlockConnector>());
|
|
}
|
|
|
|
// Create array of possible connectors for every possible height
|
|
int maxHeight = Mathf.RoundToInt(_maxHeight + _randomizeAreaHeight * _maxHeight);
|
|
_sortedConnectorPrefabs = new IBlockConnector[maxHeight][];
|
|
|
|
for (int i = 0; i < maxHeight; i++)
|
|
{
|
|
int height = i + 1;
|
|
|
|
for (int j = 0; j < maxConnectors; j++)
|
|
{
|
|
var connector = connectorPrefabs[j];
|
|
|
|
if (connector.MinHeight > height)
|
|
continue;
|
|
|
|
if (connector.MaxHeight < height)
|
|
continue;
|
|
|
|
connectors.Add(connector);
|
|
}
|
|
|
|
_sortedConnectorPrefabs[i] = connectors.ToArray();
|
|
|
|
connectors.Clear();
|
|
}
|
|
|
|
ListPool.Return(connectorPrefabs);
|
|
ListPool.Return(connectors);
|
|
}
|
|
|
|
private void SpawnConnectors()
|
|
{
|
|
for (int x = 0; x < _size; x++)
|
|
{
|
|
for (int z = 0; z < _size; z++)
|
|
{
|
|
var currentBlock = _blocks[x, z];
|
|
|
|
if (x < _size - 1)
|
|
{
|
|
var rightBlock = _blocks[x + 1, z];
|
|
PlaceConnector(new Vector2Int(x, z), currentBlock, new Vector2Int(x + 1, z), rightBlock);
|
|
}
|
|
|
|
if (z < _size - 1)
|
|
{
|
|
var topBlock = _blocks[x, z + 1];
|
|
PlaceConnector(new Vector2Int(x, z), currentBlock, new Vector2Int(x, z + 1), topBlock);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool PlaceConnector(Vector2Int aCoordinates, BlockData blockA, Vector2Int bCoordinates, BlockData blockB)
|
|
{
|
|
if (blockA.Block == null || blockB.Block == null)
|
|
return false;
|
|
|
|
var blockAPosition = blockA.Block.transform.position;
|
|
var blockBPosition = blockB.Block.transform.position;
|
|
|
|
int groundHeight = Mathf.RoundToInt(blockBPosition.y - blockAPosition.y);
|
|
|
|
Vector2Int coordinatesDirection = bCoordinates - aCoordinates;
|
|
var aHeights = blockA.Block.GetAdditionalHeights(coordinatesDirection);
|
|
var bHeights = blockB.Block.GetAdditionalHeights(-coordinatesDirection);
|
|
|
|
if (groundHeight == 0 && ((aHeights.Left == bHeights.Right && aHeights.Left >= 0) || (aHeights.Right == bHeights.Left && aHeights.Right >= 0)))
|
|
return false; // Blocks have same height at least on one half of the side
|
|
|
|
bool leftSideValid = aHeights.Left >= 0 && bHeights.Right >= 0;
|
|
bool rightSideValid = aHeights.Right >= 0 && bHeights.Left >= 0;
|
|
|
|
if (leftSideValid == false && rightSideValid == false)
|
|
return false;
|
|
|
|
int leftSideHeight = leftSideValid == true ? groundHeight - aHeights.Left + bHeights.Right : int.MaxValue;
|
|
int rightSideHeight = rightSideValid == true ? groundHeight - aHeights.Right + bHeights.Left : int.MaxValue;
|
|
|
|
bool useLeftSide = leftSideHeight != rightSideHeight ? Mathf.Abs(leftSideHeight) < Mathf.Abs(rightSideHeight) : Random.value > 0.5f;
|
|
|
|
int height = useLeftSide == true ? leftSideHeight : rightSideHeight;
|
|
bool fromAtoB = height < 0;
|
|
|
|
// We need clear space on both halfs of the lower block to use full side connector
|
|
bool canUseFullSide = fromAtoB == true ? bHeights.Left == bHeights.Right : aHeights.Left == aHeights.Right;
|
|
|
|
var connectorPrefab = GetConnector(Mathf.Abs(height), canUseFullSide == false);
|
|
if (connectorPrefab == null)
|
|
return false;
|
|
|
|
var direction = fromAtoB == true ? blockBPosition - blockAPosition : blockAPosition - blockBPosition;
|
|
direction.y = 0f;
|
|
|
|
var position = fromAtoB == true ? blockAPosition + direction * 0.5f : blockBPosition + direction * 0.5f;
|
|
position.y += fromAtoB == true ? (useLeftSide == true ? aHeights.Left : aHeights.Right) : (useLeftSide == true ? bHeights.Right : bHeights.Left);
|
|
|
|
var rotation = Quaternion.LookRotation(direction);
|
|
var scale = Vector3.one;
|
|
|
|
if (connectorPrefab.IsHalfSide == true)
|
|
{
|
|
var rightVector = -Vector3.Cross(direction, Vector3.up).normalized * _blockSize * 0.25f;
|
|
|
|
if ((fromAtoB == true && useLeftSide == true) || (fromAtoB == false && useLeftSide == false))
|
|
{
|
|
// Left side from FromBlock perspective
|
|
position -= rightVector;
|
|
scale.x = -1f; // Flip it for left side
|
|
}
|
|
else
|
|
{
|
|
// Right side from FromBlock perspective
|
|
position += rightVector;
|
|
}
|
|
}
|
|
else if (leftSideHeight == rightSideHeight)
|
|
{
|
|
// For full side with same left-right height on both blocks switch orientation randomly
|
|
scale.x = Random.value > 0.5f ? - 1f : 1f;
|
|
}
|
|
else
|
|
{
|
|
// One half of full side connector entrance is blocked, choose non blocked side (full side connectors are always oriented from right to left side)
|
|
if ((fromAtoB == true && useLeftSide == true) || (fromAtoB == false && useLeftSide == false))
|
|
{
|
|
scale.x = -1;
|
|
}
|
|
}
|
|
|
|
// Avoid Z-fighting issues
|
|
position.y -= Random.Range(0f, 0.001f);
|
|
position.x += Random.Range(-0.0005f, 0.0005f);
|
|
position.z += Random.Range(-0.0005f, 0.0005f);
|
|
|
|
var fromBlock = fromAtoB == true ? blockA : blockB;
|
|
var material = fromBlock.AreaID >= 0 ? _areas[fromBlock.AreaID].Material : null;
|
|
|
|
if (connectorPrefab.NeedsNetworkSpawn == false)
|
|
{
|
|
var connector = Instantiate(connectorPrefab as MonoBehaviour, position, rotation, transform);
|
|
connector.transform.localScale = scale;
|
|
|
|
var blockConnector = connector as IBlockConnector;
|
|
blockConnector.SetMaterial(fromBlock.AreaID, material);
|
|
blockConnector.SetHeight(Mathf.Abs(height));
|
|
}
|
|
else
|
|
{
|
|
var spawnData = new SpawnData((connectorPrefab as NetworkBehaviour), position, rotation);
|
|
spawnData.IsConnector = true;
|
|
spawnData.AreaID = fromBlock.AreaID;
|
|
spawnData.Material = material;
|
|
spawnData.Height = Mathf.Abs(height);
|
|
|
|
_objectsToSpawn.Add(spawnData);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private IBlockConnector GetConnector(int height, bool halfSideOnly)
|
|
{
|
|
int index = height - 1;
|
|
|
|
if (index < 0 || index >= _sortedConnectorPrefabs.GetLength(0))
|
|
return null;
|
|
|
|
var connectors = _sortedConnectorPrefabs[index];
|
|
|
|
if (connectors.Length == 0)
|
|
return null;
|
|
|
|
int connectorCount = connectors.Length;
|
|
int connectorIndex = Random.Range(0, connectorCount);
|
|
var connector = connectors[connectorIndex];
|
|
|
|
if (halfSideOnly == false || connector.IsHalfSide == true)
|
|
return connector;
|
|
|
|
for (int i = 1; i < connectorCount; i++)
|
|
{
|
|
connector = connectors[(connectorIndex + index) % connectorCount];
|
|
if (connector.IsHalfSide == true)
|
|
return connector;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private bool IsCoastBlock(int x, int z)
|
|
{
|
|
if (x < _size - 1)
|
|
{
|
|
if (_blocks[x + 1, z].IsFlatland == true)
|
|
return true;
|
|
|
|
if (z < _size - 1 && _blocks[x + 1, z + 1].IsFlatland == true)
|
|
return true;
|
|
|
|
if (z > 0 && _blocks[x + 1, z - 1].IsFlatland == true)
|
|
return true;
|
|
}
|
|
|
|
if (x > 0)
|
|
{
|
|
if (_blocks[x - 1, z].IsFlatland == true)
|
|
return true;
|
|
|
|
if (z < _size - 1 && _blocks[x - 1, z + 1].IsFlatland == true)
|
|
return true;
|
|
|
|
if (z > 0 && _blocks[x - 1, z - 1].IsFlatland == true)
|
|
return true;
|
|
}
|
|
|
|
if (z < _size - 1 && _blocks[x, z + 1].IsFlatland == true)
|
|
return true;
|
|
|
|
if (z > 0 && _blocks[x, z - 1].IsFlatland == true)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
private Vector2Int GetRandomAreaCenter(Vector2Int rangeStart, Vector2Int rangeEnd)
|
|
{
|
|
Vector2Int center = Vector2Int.zero;
|
|
|
|
// Just randomly try diffent centers to not overlap other areas
|
|
for (int i = 0; i < 20; i++)
|
|
{
|
|
center = new Vector2Int(Random.Range(rangeStart.x, rangeEnd.x + 1), Random.Range(rangeStart.y, rangeEnd.y + 1));
|
|
|
|
if (IsAreaCenter(center) == false)
|
|
return center;
|
|
}
|
|
|
|
// Try neighbours
|
|
int start = Random.Range(0, 8);
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
Vector2Int neighbour= center;
|
|
int index = (i + start) % 8;
|
|
|
|
switch (index)
|
|
{
|
|
case 0: neighbour.y += 1; break;
|
|
case 1: neighbour.y += 1; neighbour.x += 1; break;
|
|
case 2: neighbour.x += 1; break;
|
|
case 3: neighbour.y -= 1; neighbour.x += 1; break;
|
|
case 4: neighbour.y -= 1; break;
|
|
case 5: neighbour.y -= 1; neighbour.x -= 1; break;
|
|
case 6: neighbour.x -= 1; break;
|
|
case 7: neighbour.y += 1; neighbour.x -= 1; break;
|
|
}
|
|
|
|
if (neighbour.x < 0 || neighbour.y < 0 || neighbour.x >= _size || neighbour.y >= _size)
|
|
continue;
|
|
|
|
if (IsAreaCenter(neighbour) == false)
|
|
return neighbour;
|
|
}
|
|
|
|
Debug.LogWarning("Unique area center not found");
|
|
|
|
return center;
|
|
}
|
|
|
|
private bool IsAreaCenter(Vector2Int position)
|
|
{
|
|
for (int areaID = 0; areaID < _areaCount; areaID++)
|
|
{
|
|
var area = _areas[areaID];
|
|
|
|
if (area.Radius <= 0f) // Area (and areas after that) not initialized yet
|
|
return false;
|
|
|
|
if (area.Center == position)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// HELPERS
|
|
|
|
[System.Serializable]
|
|
public class AreaSetup
|
|
{
|
|
public LevelBlock CenterBlock;
|
|
public Material Material;
|
|
|
|
public Vector2Int Center { get; set; }
|
|
public float Radius { get; set; }
|
|
public float MaxHeight { get; set; }
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class BlockSpawn
|
|
{
|
|
public BlockSpawnItem[] Blocks;
|
|
|
|
private int _totalProbability = -1;
|
|
|
|
public LevelBlock GetBlockPrefab()
|
|
{
|
|
if (_totalProbability < 0)
|
|
{
|
|
_totalProbability = 0;
|
|
|
|
for (int i = 0; i < Blocks.Length; i++)
|
|
{
|
|
_totalProbability += Blocks[i].Probability;
|
|
}
|
|
}
|
|
|
|
if (_totalProbability == 0)
|
|
return null;
|
|
|
|
int targetProbability = Random.Range(0, _totalProbability);
|
|
int currentProbability = 0;
|
|
|
|
for (int i = 0; i < Blocks.Length; i++)
|
|
{
|
|
currentProbability += Blocks[i].Probability;
|
|
|
|
if (targetProbability < currentProbability)
|
|
{
|
|
return Blocks[i].Block;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class BlockSpawnItem
|
|
{
|
|
public LevelBlock Block;
|
|
public int Probability = 1;
|
|
}
|
|
|
|
public struct BlockData
|
|
{
|
|
public int AreaID { get { return _areaSet == true ? _areaID : -1; } set { _areaID = value; _areaSet = true; } }
|
|
public float AreaInfluence;
|
|
public bool IsFlatland;
|
|
public LevelBlock Block;
|
|
|
|
private int _areaID;
|
|
private bool _areaSet;
|
|
}
|
|
}
|
|
}
|