677 lines
25 KiB
C#
677 lines
25 KiB
C#
//$ Copyright 2015-22, Code Respawn Technologies Pvt Ltd - All Rights Reserved $//
|
|
using UnityEngine;
|
|
using System.Collections.Generic;
|
|
|
|
namespace DungeonArchitect.Builders.SimpleCity
|
|
{
|
|
public static class SimpleCityDungeonMarkerNames
|
|
{
|
|
public static readonly string House = "House";
|
|
public static readonly string Park = "Park";
|
|
public static readonly string Road_X = "Road_X";
|
|
public static readonly string Road_T = "Road_T";
|
|
public static readonly string Road_Corner = "Road_Corner";
|
|
public static readonly string Road_S = "Road_S";
|
|
public static readonly string Road_E = "Road_E";
|
|
public static readonly string Road = "Road";
|
|
|
|
public static readonly string CityWall = "CityWall";
|
|
public static readonly string CityDoor = "CityDoor";
|
|
public static readonly string CityGround = "CityGround";
|
|
public static readonly string CornerTower = "CornerTower";
|
|
public static readonly string CityWallPadding = "CityWallPadding";
|
|
}
|
|
|
|
public class SimpleCityDungeonBuilder : DungeonBuilder
|
|
{
|
|
SimpleCityDungeonConfig cityConfig;
|
|
SimpleCityDungeonModel cityModel;
|
|
|
|
new System.Random random;
|
|
/// <summary>
|
|
/// Builds the dungeon layout. In this method, you should build your dungeon layout and save it in your model file
|
|
/// No markers should be emitted here. (EmitMarkers function will be called later by the engine to do that)
|
|
/// </summary>
|
|
/// <param name="config">The builder configuration</param>
|
|
/// <param name="model">The dungeon model that the builder will populate</param>
|
|
public override void BuildDungeon(DungeonConfig config, DungeonModel model)
|
|
{
|
|
base.BuildDungeon(config, model);
|
|
|
|
random = new System.Random((int)config.Seed);
|
|
|
|
// We know that the dungeon prefab would have the appropriate config and models attached to it
|
|
// Cast and save it for future reference
|
|
cityConfig = config as SimpleCityDungeonConfig;
|
|
cityModel = model as SimpleCityDungeonModel;
|
|
cityModel.Config = cityConfig;
|
|
|
|
// Generate the city layout and save it in a model. No markers are emitted here.
|
|
GenerateCityLayout();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Override the builder's emit marker function to emit our own markers based on the layout that we built
|
|
/// You should emit your markers based on the layout you have saved in the model generated previously
|
|
/// When the user is designing the theme interactively, this function will be called whenever the graph state changes,
|
|
/// so the theme engine can populate the scene (BuildDungeon will not be called if there is no need to rebuild the layout again)
|
|
/// </summary>
|
|
public override void EmitMarkers()
|
|
{
|
|
base.EmitMarkers();
|
|
EmitCityMarkers();
|
|
EmitBoundaryMarkers();
|
|
ProcessMarkerOverrideVolumes();
|
|
}
|
|
|
|
delegate void InsertHouseDelegate();
|
|
|
|
/// <summary>
|
|
/// Generate a layout and save it in the model
|
|
/// </summary>
|
|
void GenerateCityLayout()
|
|
{
|
|
cityConfig.roadWidth = Mathf.Max(1, cityConfig.roadWidth);
|
|
var cityWidth = random.Range(cityConfig.minSize, cityConfig.maxSize);
|
|
var cityLength = random.Range(cityConfig.minSize, cityConfig.maxSize);
|
|
var roadWidth = cityConfig.roadWidth;
|
|
|
|
cityModel.CityWidth = cityWidth;
|
|
cityModel.CityHeight = cityLength;
|
|
|
|
{
|
|
cityModel.Cells = new SimpleCityCell[cityWidth, cityLength];
|
|
|
|
for (int x = 0; x < cityWidth; x++)
|
|
{
|
|
for (int z = 0; z < cityLength; z++)
|
|
{
|
|
var cell = new SimpleCityCell();
|
|
cell.Position = new IntVector(x, 0, z);
|
|
cell.CellType = SimpleCityCellType.House;
|
|
cell.Rotation = GetRandomRotation();
|
|
cityModel.Cells[x, z] = cell;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Build a road network by removing some houses
|
|
// First build roads along the edge of the map
|
|
for (int x = 0; x < cityWidth; x++)
|
|
{
|
|
MakeRoad(x, 0, true);
|
|
MakeRoad(x, cityLength - roadWidth, true);
|
|
}
|
|
for (int z = 0; z < cityLength; z++)
|
|
{
|
|
MakeRoad(0, z, false);
|
|
MakeRoad(cityWidth - roadWidth, z, false);
|
|
}
|
|
|
|
// Create roads in-between
|
|
for (int x = GetRandomBlockSize() + 1; x < cityWidth; x += GetRandomBlockSize() + 1)
|
|
{
|
|
if (cityWidth - x <= 2 * roadWidth) continue;
|
|
for (int z = 0; z < cityLength; z++)
|
|
{
|
|
MakeRoad(x, z, false);
|
|
}
|
|
}
|
|
for (int z = GetRandomBlockSize() + 1; z < cityLength; z += GetRandomBlockSize() + 1)
|
|
{
|
|
if (cityLength - z <= 2 * roadWidth) continue;
|
|
for (int x = 0; x < cityWidth; x++)
|
|
{
|
|
MakeRoad(x, z, true);
|
|
}
|
|
}
|
|
|
|
RemoveRoadEdges();
|
|
|
|
|
|
// Insert bigger houses
|
|
for (int x = 0; x < cityWidth; x++)
|
|
{
|
|
for (int z = 0; z < cityLength; z++)
|
|
{
|
|
foreach (var blockDimension in cityConfig.customBlockDimensions)
|
|
{
|
|
bool bProcess = random.NextFloat() < blockDimension.probability;
|
|
if (!bProcess) continue;
|
|
|
|
int BlockWidth = blockDimension.sizeX;
|
|
int BlockHeight = blockDimension.sizeZ;
|
|
|
|
InsertHouseDelegate InsertHouse = delegate() {
|
|
if (CanContainBiggerHouse(x, z, BlockWidth, BlockHeight))
|
|
{
|
|
if (random.NextFloat() < cityConfig.biggerHouseProbability)
|
|
{
|
|
InsertBiggerHouse(x, z, BlockWidth, BlockHeight, 0, blockDimension.markerName);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
InsertHouseDelegate InsertHouse90 = delegate ()
|
|
{
|
|
// Try the 90 degrees rotated version
|
|
if (CanContainBiggerHouse(x, z, BlockHeight, BlockWidth))
|
|
{
|
|
if (random.NextFloat() < cityConfig.biggerHouseProbability)
|
|
{
|
|
InsertBiggerHouse(x, z, BlockHeight, BlockWidth, 90, blockDimension.markerName);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (random.NextFloat() < 0.5f)
|
|
{
|
|
InsertHouse();
|
|
InsertHouse90();
|
|
}
|
|
else
|
|
{
|
|
InsertHouse90();
|
|
InsertHouse();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
for (int x = 0; x < cityWidth; x++)
|
|
{
|
|
for (int z = 0; z < cityLength; z++)
|
|
{
|
|
var cell = cityModel.Cells[x, z];
|
|
if (cell.CellType == SimpleCityCellType.House)
|
|
{
|
|
FaceHouseTowardsRoad(cell);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Create padding cells
|
|
|
|
var padding = cityConfig.cityWallPadding;
|
|
var paddedCells = new List<SimpleCityCell>();
|
|
|
|
for (int p = 1; p <= padding; p++)
|
|
{
|
|
var currentPadding = p;
|
|
|
|
var sx = -currentPadding;
|
|
var sz = -currentPadding;
|
|
var ex = cityWidth + currentPadding - 1;
|
|
var ez = cityLength + currentPadding - 1;
|
|
|
|
// Fill it with city wall padding marker
|
|
for (int x = sx; x < ex; x++)
|
|
{
|
|
SimpleCityCellType cellType = SimpleCityCellType.CityWallPadding;
|
|
|
|
paddedCells.Add(CreateCell(x, sz, cellType));
|
|
paddedCells.Add(CreateCell(x, ez, cellType));
|
|
}
|
|
|
|
for (int z = sz; z < ez; z++)
|
|
{
|
|
SimpleCityCellType cellType = SimpleCityCellType.CityWallPadding;
|
|
|
|
paddedCells.Add(CreateCell(sx, z, cellType));
|
|
paddedCells.Add(CreateCell(ex, z, cellType));
|
|
}
|
|
}
|
|
cityModel.WallPaddingCells = paddedCells.ToArray();
|
|
}
|
|
|
|
void RemoveRoadEdge(int x, int z)
|
|
{
|
|
if (!IsStraightRoad(x, z)) {
|
|
// Nothing to remove
|
|
return;
|
|
}
|
|
|
|
var RoadsToRemove = new HashSet<IntVector>();
|
|
RoadsToRemove.Add(new IntVector(x, 0, z));
|
|
int index = x - 1;
|
|
while (IsStraightRoad(index, z)) {
|
|
RoadsToRemove.Add(new IntVector(index, 0, z));
|
|
index--;
|
|
}
|
|
index = x + 1;
|
|
while (IsStraightRoad(index, z)) {
|
|
RoadsToRemove.Add(new IntVector(index, 0, z));
|
|
index++;
|
|
}
|
|
|
|
index = z - 1;
|
|
while (IsStraightRoad(x, index)) {
|
|
RoadsToRemove.Add(new IntVector(x, 0, index));
|
|
index--;
|
|
}
|
|
index = z + 1;
|
|
while (IsStraightRoad(x, index)) {
|
|
RoadsToRemove.Add(new IntVector(x, 0, index));
|
|
index++;
|
|
}
|
|
|
|
foreach (IntVector Position in RoadsToRemove) {
|
|
SimpleCityCell Cell = cityModel.Cells[Position.x, Position.z];
|
|
Cell.CellType = SimpleCityCellType.House;
|
|
}
|
|
|
|
}
|
|
|
|
bool IsStraightRoad(int x, int z) {
|
|
if (GetCellType(x, z) != SimpleCityCellType.Road) {
|
|
return false;
|
|
}
|
|
|
|
bool bTop = GetCellType(x, z - 1) == SimpleCityCellType.Road;
|
|
bool bBottom = GetCellType(x, z + 1) == SimpleCityCellType.Road;
|
|
bool bLeft = GetCellType(x - 1, z) == SimpleCityCellType.Road;
|
|
bool bRight = GetCellType(x + 1, z) == SimpleCityCellType.Road;
|
|
|
|
bool bHorizontal = bLeft && bRight;
|
|
bool bVertical = bTop && bBottom;
|
|
|
|
int Adjacent = 0;
|
|
if (bTop) Adjacent++;
|
|
if (bBottom) Adjacent++;
|
|
if (bLeft) Adjacent++;
|
|
if (bRight) Adjacent++;
|
|
|
|
if (Adjacent != 2) return false;
|
|
|
|
return bHorizontal || bVertical;
|
|
}
|
|
|
|
void RemoveRoadEdges() {
|
|
int Width = cityModel.CityWidth;
|
|
int Length = cityModel.CityHeight;
|
|
|
|
for (int x = 0; x < Width; x++) {
|
|
for (int z = 0; z < Length; z++) {
|
|
if (IsStraightRoad(x, z)) {
|
|
bool bRemove = random.NextFloat() < cityConfig.roadEdgeRemovalProbability;
|
|
if (bRemove) {
|
|
RemoveRoadEdge(x, z);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
// Remove the isolated road cells
|
|
for (int x = 0; x < Width; x++) {
|
|
for (int z = 0; z < Length; z++) {
|
|
if (GetCellType(x, z) == SimpleCityCellType.Road) {
|
|
int Adjacent = 0;
|
|
if (GetCellType(x, z - 1) == SimpleCityCellType.Road) Adjacent++;
|
|
if (GetCellType(x, z + 1) == SimpleCityCellType.Road) Adjacent++;
|
|
if (GetCellType(x - 1, z) == SimpleCityCellType.Road) Adjacent++;
|
|
if (GetCellType(x + 1, z) == SimpleCityCellType.Road) Adjacent++;
|
|
if (Adjacent == 0) {
|
|
// No adjacent roads connecting to this road cell. remove it
|
|
SimpleCityCell Cell = demoModel.Cells[x, z];
|
|
Cell.CellType = SimpleCityCellType.House;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
SimpleCityCell CreateCell(int x, int z, SimpleCityCellType cellType)
|
|
{
|
|
var cell = new SimpleCityCell();
|
|
cell.Position = new IntVector(x, 0, z);
|
|
cell.CellType = cellType;
|
|
cell.Rotation = Quaternion.identity;
|
|
return cell;
|
|
}
|
|
|
|
SimpleCityCellType GetCellType(int x, int z) {
|
|
if (x < 0 || x >= cityModel.Cells.GetLength(0) ||
|
|
z < 0 || z >= cityModel.Cells.GetLength(1)) {
|
|
return SimpleCityCellType.Empty;
|
|
}
|
|
return cityModel.Cells[x, z].CellType;
|
|
}
|
|
|
|
void FaceHouseTowardsRoad(SimpleCityCell cell) {
|
|
int x = cell.Position.x;
|
|
int z = cell.Position.z;
|
|
|
|
bool roadLeft = GetCellType(x - 1, z) == SimpleCityCellType.Road;
|
|
bool roadRight = GetCellType(x + 1, z) == SimpleCityCellType.Road;
|
|
bool roadTop = GetCellType(x, z - 1) == SimpleCityCellType.Road;
|
|
bool roadBottom = GetCellType(x, z + 1) == SimpleCityCellType.Road;
|
|
|
|
if (!roadLeft && !roadRight && !roadTop && !roadBottom) {
|
|
// No roads nearby. promote to park
|
|
cell.CellType = SimpleCityCellType.Park;
|
|
cell.Rotation = Quaternion.Euler(0, 90 * (random.Next() % 4), 0);
|
|
return;
|
|
}
|
|
|
|
float angle = 0;
|
|
if (roadLeft) angle = 0;
|
|
else if (roadRight) angle = 180;
|
|
else if (roadTop) angle = 270;
|
|
else if (roadBottom) angle = 90;
|
|
|
|
cell.Rotation = Quaternion.Euler(0, angle, 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Make sure the 2x2 grid is occupied by 4 1x1 houses, so we can replace theme with a single bigger house
|
|
/// </summary>
|
|
/// <param name="x"></param>
|
|
/// <param name="z"></param>
|
|
/// <returns></returns>
|
|
bool CanContainBiggerHouse(int x, int z, int w, int h)
|
|
{
|
|
int cityWidth = cityModel.Cells.GetLength(0);
|
|
int cityLength = cityModel.Cells.GetLength(1);
|
|
for (int dx = 0; dx < w; dx++)
|
|
{
|
|
for (int dz = 0; dz < h; dz++)
|
|
{
|
|
if (x + dx >= cityWidth || z + dz >= cityLength)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var cell = cityModel.Cells[x + dx, z + dz];
|
|
if (cell.CellType != SimpleCityCellType.House)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// The house can fit in this area.
|
|
// Make sure this house is connected to the road
|
|
bool connectedToRoad = IsConnectedToRoad(x, z, w, h);
|
|
return connectedToRoad ;
|
|
}
|
|
|
|
bool IsConnectedToRoad(int x, int z, int w, int h)
|
|
{
|
|
int cityWidth = cityModel.Cells.GetLength(0);
|
|
int cityLength = cityModel.Cells.GetLength(1);
|
|
|
|
var samplePoints = new List<IntVector>();
|
|
for (int dx = 0; dx < w; dx++)
|
|
{
|
|
int[] dz = new int[] { z - 1, z + h };
|
|
for (int dzi = 0; dzi < 2; dzi++)
|
|
{
|
|
int xx = x + dx;
|
|
int zz = dz[dzi];
|
|
samplePoints.Add(new IntVector(xx, 0, zz));
|
|
}
|
|
}
|
|
|
|
for (int dz = 0; dz < w; dz++)
|
|
{
|
|
int[] dx = new int[] { x - 1, x + w };
|
|
for (int dxi = 0; dxi < 2; dxi++)
|
|
{
|
|
int xx = dx[dxi];
|
|
int zz = z + dz;
|
|
samplePoints.Add(new IntVector(xx, 0, zz));
|
|
}
|
|
}
|
|
|
|
foreach (var samplePoint in samplePoints)
|
|
{
|
|
int xx = samplePoint.x;
|
|
int zz = samplePoint.z;
|
|
if (xx < 0 || xx >= cityWidth || zz < 0 || zz >= cityLength)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var cell = cityModel.Cells[xx, zz];
|
|
if (cell.CellType == SimpleCityCellType.Road)
|
|
{
|
|
// Connected to a road
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// No adjacent road cells found
|
|
return false;
|
|
}
|
|
/// <summary>
|
|
/// Replaces the 4 1x1 smaller houses with a single 2x2 bigger house. Assumes that there are 4 houses in x,z to x+1,z+1
|
|
/// </summary>
|
|
/// <param name="x"></param>
|
|
/// <param name="z"></param>
|
|
void InsertBiggerHouse(int x, int z, int w, int h, float Angle, string markerName)
|
|
{
|
|
for (int dx = 0; dx < w; dx++)
|
|
{
|
|
for (int dz = 0; dz < h; dz++)
|
|
{
|
|
var cell = cityModel.Cells[x + dx, z + dz];
|
|
if (dx == 0 && dz == 0)
|
|
{
|
|
cell.CellType = SimpleCityCellType.UserDefined;
|
|
cell.Rotation = Quaternion.Euler(0, Angle, 0);
|
|
cell.BlockSize = new Vector3(w, 0, h);
|
|
cell.MarkerNameOverride = markerName;
|
|
}
|
|
else
|
|
{
|
|
// Make these cells empty, as they will be occupied by the bigger house and we don't want any markers here
|
|
cell.CellType = SimpleCityCellType.Empty;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Turns a house cell into a road
|
|
/// </summary>
|
|
/// <param name="cell"></param>
|
|
void MakeRoad(int x, int z, bool horizontal)
|
|
{
|
|
var dx = horizontal ? 0 : 1;
|
|
var dz = horizontal ? 1 : 0;
|
|
for (int d = 0; d < cityConfig.roadWidth; d++)
|
|
{
|
|
var ix = x + d * dx;
|
|
var iz = z + d * dz;
|
|
ix = Mathf.Clamp(ix, 0, cityModel.CityWidth - 1);
|
|
iz = Mathf.Clamp(iz, 0, cityModel.CityHeight - 1);
|
|
var cell = cityModel.Cells[ix, iz];
|
|
cell.CellType = SimpleCityCellType.Road;
|
|
cell.Rotation = Quaternion.identity;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Emit marker points so that the theme can decorate the scene layout that we just built
|
|
/// </summary>
|
|
void EmitCityMarkers()
|
|
{
|
|
var basePosition = transform.position;
|
|
var cells = cityModel.Cells;
|
|
var width = cells.GetLength(0);
|
|
var length = cells.GetLength(1);
|
|
var cellSize = new Vector3(cityConfig.CellSize.x, 0, cityConfig.CellSize.y);
|
|
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
for (int z = 0; z < length; z++)
|
|
{
|
|
var cell = cells[x, z];
|
|
string markerName = "Unknown";
|
|
|
|
Quaternion rotation = Quaternion.identity;
|
|
var worldPosition = cell.Position * cellSize + basePosition;
|
|
|
|
if (cell.CellType == SimpleCityCellType.House)
|
|
{
|
|
markerName = SimpleCityDungeonMarkerNames.House;
|
|
rotation = cell.Rotation;
|
|
}
|
|
else if (cell.CellType == SimpleCityCellType.UserDefined)
|
|
{
|
|
markerName = cell.MarkerNameOverride;
|
|
worldPosition += Vector3.Scale(cell.BlockSize / 2.0f - new Vector3(0.5f, 0, 0.5f), cellSize);
|
|
rotation = cell.Rotation;
|
|
}
|
|
else if (cell.CellType == SimpleCityCellType.Park)
|
|
{
|
|
markerName = SimpleCityDungeonMarkerNames.Park;
|
|
rotation = cell.Rotation;
|
|
}
|
|
else if (cell.CellType == SimpleCityCellType.Road)
|
|
{
|
|
float angle = 0;
|
|
markerName = RoadBeautifier.GetRoadMarkerName(x, z, cells, out angle);
|
|
rotation = Quaternion.Euler(0, angle, 0);
|
|
}
|
|
|
|
var markerTransform = Matrix4x4.TRS(worldPosition, rotation, Vector3.one);
|
|
EmitMarker(markerName, markerTransform, cell.Position, -1);
|
|
|
|
// Emit the generic road marker
|
|
if (cell.CellType == SimpleCityCellType.Road)
|
|
{
|
|
EmitMarker(SimpleCityDungeonMarkerNames.Road, markerTransform, cell.Position, -1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void EmitBoundaryMarkers()
|
|
{
|
|
var config = cityModel.Config;
|
|
var cells = cityModel.Cells;
|
|
|
|
var padding = config.cityWallPadding;
|
|
var doorSize = config.cityDoorSize;
|
|
|
|
var width = cells.GetLength(0);
|
|
var length = cells.GetLength(1);
|
|
|
|
var cellSize = new Vector3(config.CellSize.x, 0, config.CellSize.y);
|
|
for (int p = 1; p <= padding; p++)
|
|
{
|
|
|
|
var currentPadding = p;
|
|
|
|
var sx = -currentPadding;
|
|
var sz = -currentPadding;
|
|
var ex = width + currentPadding - 1;
|
|
var ez = length + currentPadding - 1;
|
|
|
|
if (currentPadding == padding)
|
|
{
|
|
var halfDoorSize = doorSize / 2.0f;
|
|
// Insert markers along the 4 wall sides
|
|
for (float x = sx; x < ex; x++)
|
|
{
|
|
if ((int)x == (int)((sx + ex) / 2 - halfDoorSize))
|
|
{
|
|
EmitDoorMarker(cellSize, x + halfDoorSize, sz, 0);
|
|
EmitDoorMarker(cellSize, x + halfDoorSize, ez, 180);
|
|
x += halfDoorSize;
|
|
continue;
|
|
}
|
|
EmitWallMarker(cellSize, x + 0.5f, sz, 0);
|
|
EmitWallMarker(cellSize, x + 0.5f, ez, 180);
|
|
}
|
|
|
|
for (float z = sz; z < ez; z++)
|
|
{
|
|
if ((int)z == (int)((sz + ez) / 2 - halfDoorSize))
|
|
{
|
|
EmitDoorMarker(cellSize, sx, z + halfDoorSize, 90);
|
|
EmitDoorMarker(cellSize, ex, z + halfDoorSize, 270);
|
|
z += halfDoorSize;
|
|
continue;
|
|
}
|
|
EmitWallMarker(cellSize, sx, z + 0.5f, 90);
|
|
EmitWallMarker(cellSize, ex, z + 0.5f, 270);
|
|
}
|
|
|
|
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CornerTower, sx, sz, 0);
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CornerTower, ex + 0.5f, sz, 0);
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CornerTower, sx, ez + 0.5f, 0);
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CornerTower, ex + 0.5f, ez + 0.5f, 0);
|
|
}
|
|
else
|
|
{
|
|
// Fill it with city wall padding marker
|
|
for (float x = sx; x < ex; x++)
|
|
{
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CityWallPadding, x + 0.5f, sz, 0);
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CityWallPadding, x + 0.5f, ez + 0.5f, 180);
|
|
}
|
|
|
|
for (float z = sz; z < ez; z++)
|
|
{
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CityWallPadding, sx, z + 0.5f, 90);
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CityWallPadding, ex + 0.5f, z + 0.5f, 270);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Emit a ground marker since the city builder doesn't emit any ground.
|
|
// The theme can add a plane here if desired (won't be needed if building on a landscape)
|
|
EmitGroundMarker(width, length, cellSize);
|
|
|
|
}
|
|
|
|
void EmitWallMarker(Vector3 cellSize, float x, float z, float angle)
|
|
{
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CityWall, x, z, angle);
|
|
}
|
|
|
|
void EmitDoorMarker(Vector3 cellSize, float x, float z, float angle)
|
|
{
|
|
EmitMarkerAt(cellSize, SimpleCityDungeonMarkerNames.CityDoor, x, z, angle);
|
|
}
|
|
|
|
void EmitGroundMarker(int sizeX, int sizeZ, Vector3 cellSize)
|
|
{
|
|
var position = Vector3.Scale(new Vector3(sizeX, 0, sizeZ) / 2.0f, cellSize) + transform.position;
|
|
var scale = new Vector3(sizeX, 1, sizeZ);
|
|
var trans = Matrix4x4.TRS(position, Quaternion.identity, scale);
|
|
EmitMarker(SimpleCityDungeonMarkerNames.CityGround, trans, IntVector.Zero, -1);
|
|
}
|
|
|
|
void EmitMarkerAt(Vector3 cellSize, string markerName, float x, float z, float angle)
|
|
{
|
|
var worldPosition = Vector3.Scale(new Vector3(x, 0, z), cellSize) + transform.position;
|
|
var rotation = Quaternion.Euler(0, angle, 0);
|
|
var transformation = Matrix4x4.TRS(worldPosition, rotation, Vector3.one);
|
|
var gridPosition = new IntVector((int)x, 0, (int)z); // Optionally provide where this marker is in the grid position
|
|
EmitMarker(markerName, transformation, gridPosition, -1);
|
|
}
|
|
|
|
Quaternion GetRandomRotation()
|
|
{
|
|
// Randomly rotate in steps of 90
|
|
var angle = random.Next(0, 4) * 90;
|
|
return Quaternion.Euler(0, angle, 0);
|
|
}
|
|
|
|
int GetRandomBlockSize()
|
|
{
|
|
return random.Next(cityConfig.minBlockSize, cityConfig.maxBlockSize + 1);
|
|
}
|
|
}
|
|
} |