private void GenerateRiver(Vec2i start) { Vec2i end = null; for (int i = 1; i < 100; i++) { if (end != null) { break; } for (int j = 0; j < DIRS.Length; j++) { Vec2i pos = start + DIRS[j] * i; if (GameGenerator2.InBounds(pos)) { if (GameGen.TerGen.ChunkBases[pos.x, pos.z].Biome == ChunkBiome.ocean) { end = pos; break; } } } } if (end == null) { return; } LineI li = new LineI(start, end); List <Vec2i> basic = li.ConnectPoints(); float val1 = GenRan.Random(0.1f, 0.001f); float val2 = GenRan.Random(0.1f, 0.001f); int val3 = GenRan.RandomInt(8, 32); int val4 = GenRan.RandomInt(8, 32); for (int i = 0; i < basic.Count; i++) { basic[i] += new Vec2i((int)(val3 * Mathf.Sin(i * val1)), (int)(val4 * Mathf.Sin(i * val2))); } for (int i = 0; i < basic.Count - 1; i++) { Vec2i v1 = basic[i]; Vec2i v2 = basic[i + 1]; LineI i2 = new LineI(v1, v2); foreach (Vec2i v in i2.ConnectPoints()) { GameGen.TerGen.ChunkBases[v.x, v.z].SetChunkFeature(new ChunkRiverNode(v)); } } return; }
/// <summary> /// Defines the bounds of the building to have an L shape. /// /// </summary> /// <param name="genRan">RNG used to generate building</param> /// <param name="smith">Building shell to define wall of</param> private static void ChooseLShapedWall(GenerationRandom genRan, Blacksmith smith) { //Define a base rectangular wall List <Vec2i> wallPoints = new List <Vec2i>(); wallPoints.Add(new Vec2i(0, 0)); wallPoints.Add(new Vec2i(smith.Width - 1, 0)); wallPoints.Add(new Vec2i(smith.Width - 1, smith.Height - 1)); wallPoints.Add(new Vec2i(0, smith.Height - 1)); //Choose which point index we wish to move int deltaPointI = genRan.RandomInt(0, wallPoints.Count); //We find which direction and amount the point has to move to not leave the bounds int xDir = (wallPoints[deltaPointI].x == 0 ? 1 : -1) * genRan.RandomInt(smith.Width / 3, smith.Width - smith.Width / 3); int zDir = (wallPoints[deltaPointI].z == 0 ? 1 : -1) * genRan.RandomInt(smith.Height / 3, smith.Height - smith.Height / 3); Vec2i newPoint = new Vec2i(xDir, zDir) + wallPoints[deltaPointI]; //wallPoints.Insert(deltaPointI-1, newPoint); //We find the direction from the previous corner to this one, //we use this to translate the point accordingly Vec2i nm1_to_n = wallPoints[deltaPointI] - wallPoints[(deltaPointI - 1 + wallPoints.Count) % wallPoints.Count]; Vec2i np1_to_n = wallPoints[(deltaPointI + 1) % wallPoints.Count] - wallPoints[deltaPointI]; if (nm1_to_n.x == 0) { wallPoints[deltaPointI].z = newPoint.z; } else { wallPoints[deltaPointI].x = newPoint.x; } // Vec2i np1_to_n = wallPoints[(deltaPointI + 1) % wallPoints.Count] - wallPoints[deltaPointI]; Vec2i newPoint2 = new Vec2i(newPoint.x, newPoint.z); if (np1_to_n.x == 0) { newPoint2.x = wallPoints[(deltaPointI + 1) % wallPoints.Count].x; } else { newPoint2.z = wallPoints[(deltaPointI + 1) % wallPoints.Count].z; } wallPoints.Insert((deltaPointI + 1), newPoint); wallPoints.Insert((deltaPointI + 2), newPoint2); smith.BoundingWall = wallPoints.ToArray(); }
/// <summary> /// Places a roof over the specified building. /// The roof will only cover the tile specified by 'roofTileID'. If this is null, /// then a roof over the total building will be created /// </summary> /// <param name="vox"></param> /// <param name="build"></param> /// <param name="roofTileID"></param> public static void AddRoof(GenerationRandom genRan, BuildingVoxels vox, Building build, Voxel roofVox, int roofStartY = 5, Tile roofTileReplace = null) { if (roofTileReplace == null) { Vec2i dir = genRan.RandomQuadDirection(); dir.x = Mathf.Abs(dir.x); dir.z = Mathf.Abs(dir.z); Vec2i start = new Vec2i(build.Width / 2 * dir.x, build.Height / 2 * dir.z); Vec2i end = start + new Vec2i(dir.z * build.Width, dir.x * build.Height); LineI t = new LineI(start, end); float maxDist = Mathf.Max(t.Distance(new Vec2i(0, 0)), t.Distance(new Vec2i(build.Width - 1, 0)), t.Distance(new Vec2i(0, build.Height - 1)), t.Distance(new Vec2i(build.Width - 1, build.Height - 1))); int maxHeight = genRan.RandomInt(4, 6); float scale = maxHeight / (maxDist + 1); //float scale = 0.7f; for (int x = 0; x < build.Width; x++) { for (int z = 0; z < build.Height; z++) { int height = (int)Mathf.Clamp((maxDist - t.Distance(new Vec2i(x, z)) + 1) * scale, 1, World.ChunkHeight - 1); for (int y = 0; y < height; y++) { vox.AddVoxel(x, Mathf.Clamp(roofStartY + y, 0, World.ChunkHeight - 1), z, roofVox); } } } } }
public override WorldObject CreateWorldObject(Transform transform = null) { GenerationRandom genRan = new GenerationRandom(WorldPosition.GetHashCode() * 13); int count = genRan.RandomInt(2, 5); WorldObject r1 = null; for (int i = 0; i < count; i++) { Vector3 pos = genRan.RandomVector3(-MaxSize, MaxSize, (i) * 0.1f, (i) * 0.2f + 0.1f, -MaxSize, MaxSize) / 100f; float rSize = genRan.Random(0.5f, 1.5f); Rock r = new Rock(WorldPosition, pos, rSize); if (i == 0) { r1 = r.CreateWorldObject(transform); } else { WorldObject rn = r.CreateWorldObject(r1.transform); rn.transform.localPosition = pos; } } return(r1); }
/// <summary> /// Generates a quest with the goal of clearing a specific chunk structure /// </summary> /// <param name="dungeonPos"></param> /// <param name="chunkStructure"></param> /// <returns></returns> private Quest GenerateDungeonQuest(Vec2i dungeonPos, ChunkStructure chunkStructure) { questCount++; //Get the dungeon this quest is based on Dungeon dun = chunkStructure.DungeonEntrance.GetDungeon(); //If the dungeon doesn't have a key (generation should be set so it doesn't), we create one Key key = dun.GetKey(); if (key == null) { key = new SimpleDungeonKey(GenRan.RandomInt(1, int.MaxValue)); dun.SetKey(key); } //We build the quest in reverse, we first define the goal List <QuestTask> revTasks = new List <QuestTask>(); //For a dungeon quest, this is always to kill the dungeon boss revTasks.Add(new QuestTask("Kill " + dun.Boss.Name, QuestTask.QuestTaskType.KILL_ENTITY, new object[] { dun.Boss })); //The second to last task is to get to the dungeon, revTasks.Add(new QuestTask("Get to dungeon", QuestTask.QuestTaskType.GO_TO_LOCATION, new object[] { chunkStructure.WorldMapLocation, dun.WorldEntrance })); //Before we get to the dungeon, we need to get the key. //The key can either be held by an entity, or it can be found in a //random chunk structure with no dungeon. return(GenerateDungeonKeyQuest_RandomStructure(dungeonPos, dun, revTasks)); }
/// <summary> /// Generates the simplest type of house - a shack /// A shack is made of either (cobble?) stone or wood, /// it consists of only 1 room, with basic objects inside (bed, chair, table) /// </summary> /// <param name="house"></param> /// <param name="vox"></param> /// <returns></returns> public static House GenerateShack(GenerationRandom genRan, House house, out BuildingVoxels vox, BuildingGenerationPlan plan) { vox = new BuildingVoxels(house.Width, World.ChunkHeight, house.Height); Tile[,] floor = new Tile[house.Width, house.Height]; BuildingGenerator.BuildBoundingWallRect(vox, house.Width, house.Height, 6, Voxel.wood); //BuildingGenerator.ConnectBoundingWall(vox, house.BoundingWall, Voxel.wood, 5); BuildingGenerator.SetTiles(floor, 0, 0, house.Width - 1, house.Height - 1, Tile.STONE_FLOOR); house.SetBuilding(floor); BuildingGenerator.ChooseEntrancePoint(genRan, vox, house, plan); BuildingGenerator.AddWindow(genRan, vox, house, size: genRan.RandomInt(1, 4), autoReattempt: true); BuildingGenerator.AddWindow(genRan, vox, house, size: genRan.RandomInt(1, 4), autoReattempt: true); BuildingGenerator.AddWindow(genRan, vox, house, size: genRan.RandomInt(1, 4), autoReattempt: false); BuildingGenerator.AddWindow(genRan, vox, house, size: genRan.RandomInt(1, 4), autoReattempt: false); BuildingGenerator.AddWindow(genRan, vox, house, size: genRan.RandomInt(1, 4), autoReattempt: false); house.InsideWallPoints = BuildingGenerator.FindInsideWallBoundryPoints(vox, house); /*foreach (Vec2i v in house.InsideWallPoints) * { * //house.AddObjectReference(new Chest().SetPosition(v)); * WorldObjectData bed = new Bed().SetPosition(v); * bed.SetRotation(Vec2i.Angle(Vec2i.Forward, BuildingGenerator.GetWallPointDirection(house, v))); * if(!house.ObjectIntersects(bed)) * house.AddObjectReference(bed); * }*/ BuildingGenerator.PlaceObjectAgainstWall(genRan, new DoubleBed(), 0, vox, house, .3f, distToEntr: 4, attemptsCount: 40); BuildingGenerator.PlaceObjectAgainstWall(genRan, new Chest(), 0, vox, house, 0.1f, distToEntr: 3, attemptsCount: 40); for (int i = 0; i < house.BoundingWall.Length; i++) { BuildingGenerator.PlaceObjectAgainstWall(genRan, new WallTorch(), 1.5f, vox, house, 0f, requireWallBacking: true); } house.AddExternalObject(new BuildingInternalNoWalkFloor(new Vector3(1, 0, 1), new Vector3(house.Width - 2, 5, house.Height - 2))); BuildingGenerator.AddRoof(genRan, vox, house, Voxel.thatch); BuildingSubworldBuilder b = new BuildingSubworldBuilder(house, vox, plan); b.CreateSubworld(); //ChunkData[,] subChunks = new ChunkData[1, 1]; return(house); }
/// <summary> /// We override the create world object function for a tree. This is done as we need to load in multiple /// parts of the tree /// </summary> /// <param name="transform"></param> /// <returns></returns> public override WorldObject CreateWorldObject(Transform transform = null) { //Create random with seed unique to tree position - this ensures tree should be same every time. GenerationRandom genRan = new GenerationRandom(WorldPosition.x * World.WorldSize * World.ChunkSize + WorldPosition.z); int canopy = genRan.RandomInt(3, 7); float angleOffset = genRan.Random(0, Mathf.PI); float range = Mathf.Sqrt(canopy * 0.5f) * 0.8f; WorldObject treeBase = WorldObject.CreateWorldObject(this, transform); Vec2i zero = new Vec2i(0, 0); Vector3[] canopyPositions = new Vector3[canopy]; for (int i = 0; i < canopy; i++) { //Define the positions of each canopy as roughly circular about the middle of the tree. float angle = (Mathf.PI * 2 / (canopy)) * i + angleOffset; Vector2 delta = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * range + genRan.RandomVector2(-0.1f, 0.1f); float height = 1.8f + genRan.Random(0, 1f); //Store the total position for use later, then generate the canopy itself. canopyPositions[i] = new Vector3(delta.x, height, delta.y); TreeCanopy tr = new TreeCanopy(zero, canopyPositions[i]); tr.SetRandom(genRan); tr.CreateWorldObject(treeBase.transform); } //Generate the trunk float trunkHeight = 1.2f; float trunkScale = 1.2f; TreeBranch trunk = new TreeBranch(zero); trunk.SetCharacteristic(Vector3.zero, new Vector3(0, 0, 0), new Vector3(1, trunkScale, 1)); WorldObject trunkObj = trunk.CreateWorldObject(treeBase.transform); //MeshCollider mc =trunkObj.gameObject.AddComponent<MeshCollider>(); //mc.sharedMesh = trunkObj.gameObject.GetComponent<MeshFilter>().mesh; Vector3 trunkTop = Vector3.up * trunkHeight * trunkScale; //Scale to correct height foreach (Vector3 v in canopyPositions) { TreeBranch canopyBranch = new TreeBranch(zero); //Calculate the Euler angles from the branch base to the canopy Vector3 delta_pos = v - trunkTop; Quaternion.FromToRotation(Vector3.up, delta_pos); //Vector3 rot = Quaternion.FromToRotation(trunkTop, v*5f).eulerAngles; Vector3 rot = Quaternion.FromToRotation(Vector3.up, delta_pos).eulerAngles; float scale = Vector3.Distance(v, trunkTop) / trunkHeight; canopyBranch.SetCharacteristic(trunkTop, rot, new Vector3(1 / scale, scale, 1 / scale)); canopyBranch.CreateWorldObject(treeBase.transform); } return(treeBase); }
public static Building CreateBuilding(GenerationRandom genRan, out BuildingVoxels vox, BuildingGenerationPlan plan) { Vec2i zero = new Vec2i(0, 0); int maxWidth = (plan.DesiredSize == null || plan.DesiredSize == zero) ?Mathf.Min(plan.BuildingPlan.MaxSize, plan.MaxWidth): plan.DesiredSize.x; int maxHeight = (plan.DesiredSize == null || plan.DesiredSize == zero)? Mathf.Min(plan.BuildingPlan.MaxSize, plan.MaxHeight) : plan.DesiredSize.z; int width = genRan.RandomInt(plan.BuildingPlan.MinSize, maxWidth); int height = genRan.RandomInt(plan.BuildingPlan.MinSize, maxHeight); if (plan.BuildingPlan == Building.BLACKSMITH) { Blacksmith smith = BlacksmithGenerator.GenerateBlacksmith(genRan, new Blacksmith(width, height), out vox, plan); return(smith); } if (plan.BuildingPlan == Building.BARACKS) { Barracks barr = BarracksGenerator.GenerateBarracks(genRan, new Barracks(width, height), out vox, plan); return(barr); } if (plan.BuildingPlan == Building.TAVERN) { return(TavernGenerator.GenerateTavern(genRan, new Tavern(width, height), out vox, plan)); } if (plan.BuildingPlan == Building.VEGFARM) { Farm farm = FarmGenerator.GenerateVegFarm(genRan, new Farm(width, height), out vox, plan); return(farm); } if (plan.BuildingPlan == Building.WHEATFARM) { Farm farm = FarmGenerator.GenerateWheatFarm(genRan, new Farm(width, height), out vox, plan); return(farm); } House house = HouseGenerator.GenerateHouse(genRan, new House(width, height), out vox, plan); return(house); //return GenerateHouse(out vox, width, height); }
/// <summary> /// Places a small amount of large paths /// </summary> private void PlaceMainPaths(int initialPathCount = 9) { //Calculate node map size MapNodeSize = TileSize / NodeSize; NodeMap = new bool[MapNodeSize, MapNodeSize]; BuildingMap = new bool[MapNodeSize, MapNodeSize]; Path = new bool[TileSize, TileSize]; AllNodes = new List <Vec2i>(); Vec2i startDir; Vec2i entranceToMid = Middle - Entrance; //Find the entrance direction if (Mathf.Abs(entranceToMid.x) > Mathf.Abs(entranceToMid.z)) { startDir = new Vec2i((int)Mathf.Sign(entranceToMid.x), 0); } else { startDir = new Vec2i(0, (int)Mathf.Sign(entranceToMid.z)); } //The start direction for our path EntranceSide = startDir; //Calculate a random length int length = GenerationRandom.RandomInt(Shell.Type.GetSize() / 2 - 1, Shell.Type.GetSize() - 1) * NodeSize; Vec2i nodeEntr = Vec2i.FromVector2(new Vector2((float)Entrance.x / NodeSize, (float)Entrance.z / NodeSize)) * NodeSize; //Place 'main road' AddPath(nodeEntr, startDir, length, 5, NodeMap, AllNodes, NodeSize, true); //We generate other initial paths for (int i = 0; i < initialPathCount - 1; i++) { GeneratePathBranch(AllNodes, width: 3); } }
/// <summary> /// Defines the building shape to be rectangular, with a rectangular section defining outside, /// and another defining inside /// </summary> /// <param name="genRan"></param> /// <param name="smith"></param> private static void ChooseHalfCutWall(GenerationRandom genRan, Blacksmith smith) { //Define a base rectangular wall List <Vec2i> wallPoints = new List <Vec2i>(); wallPoints.Add(new Vec2i(0, 0)); wallPoints.Add(new Vec2i(smith.Width - 1, 0)); wallPoints.Add(new Vec2i(smith.Width - 1, smith.Height - 1)); wallPoints.Add(new Vec2i(0, smith.Height - 1)); int wallIndex = genRan.RandomInt(0, 4); int wp1 = (wallIndex + 1) % 4; wallPoints[wallIndex].x = (int)(wallPoints[wallIndex].x * 0.5f); wallPoints[wallIndex].z = (int)(wallPoints[wallIndex].z * 0.5f); wallPoints[wp1].x = (int)(wallPoints[wp1].x * 0.5f); wallPoints[wp1].z = (int)(wallPoints[wp1].z * 0.5f); smith.BoundingWall = wallPoints.ToArray(); }
/// <summary> /// Generates all empty NPCs, defining only their name, and thier homes /// </summary> /// <param name="set"></param> private List <NPC> GenerateNPCShells(Settlement set) { if (set.Buildings == null) { return(null); } List <NPC> settlementNPCs = new List <NPC>(250); List <NPC> inThisHouse = new List <NPC>(10); //iterate all building, and check if this is a house foreach (Building b in set.Buildings) { if (b is House) { inThisHouse.Clear(); //Define the house and get the possible tiles we can spawn the this houses' NPCs on House h = b as House; List <Vec2i> spawnableTiles = h.GetSpawnableTiles(); for (int i = 0; i < h.capacity; i++) { //Attempt to find a valid spawn point Vec2i entitySpawn = MiscUtils.RandomFromArray(spawnableTiles, Random.value); if (entitySpawn == null) { Debug.Log("No valid spawn point found for house " + h.ToString(), Debug.ENTITY_GENERATION); continue; } //Create the empty NPC and add it to the settlement NPC npc = new NPC(isFixed: true); npc.SetPosition(entitySpawn); npc.NPCData.SetHome(h); //First two entities should be male and female (husband and wife) //All others are then randomly assigned if (i == 0) { npc.NPCData.SetGender(NPCGender.male); } else if (i == 1) { npc.NPCData.SetGender(NPCGender.female); } else { npc.NPCData.SetGender((NPCGender)GenerationRan.RandomInt(0, 2)); } EntityManager.AddFixedEntity(npc); npc.SetName("NPC " + npc.ID); set.AddNPC(npc); settlementNPCs.Add(npc); inThisHouse.Add(npc); } //If more than 1 person lives in this house, they are family if (inThisHouse.Count > 1) { //Iterate all pairs of family members in this house for (int i = 0; i < inThisHouse.Count; i++) { for (int j = 0; j < inThisHouse.Count; j++) { if (i == j) { continue; } inThisHouse[i].EntityRelationshipManager.SetRelationshipTag(inThisHouse[j], EntityRelationshipTag.Family); inThisHouse[j].EntityRelationshipManager.SetRelationshipTag(inThisHouse[i], EntityRelationshipTag.Family); } } } } } return(settlementNPCs); }
public static bool AddWindow(GenerationRandom genRan, BuildingVoxels vox, Building build, int wallIndex = -1, int size = 2, int height = 1, bool autoReattempt = true, int reattemptCount = 3) { reattemptCount--; if (reattemptCount == 0) { return(false); } //if default index, we choose one randomly if (wallIndex == -1) { wallIndex = genRan.RandomInt(0, build.BoundingWall.Length); } int wp1 = (wallIndex + 1) % build.BoundingWall.Length; Vec2i dif = build.BoundingWall[wp1] - build.BoundingWall[wallIndex]; int absX = Mathf.Abs(dif.x); int signX = (int)Mathf.Sign(dif.x); int absZ = Mathf.Abs(dif.z); int signZ = (int)Mathf.Sign(dif.z); Vec2i dir = new Vec2i(absX == 0?0:signX, absZ == 0 ? 0 : signZ); int len = absX + absZ; //if the selected wall has a length that is not large enough to house the window, if (len < size + 4) { if (autoReattempt) { return(AddWindow(genRan, vox, build, wp1, size, height, autoReattempt, reattemptCount)); } //Then we do not build a window return(false); } int dx = dir.x == 0 ? 0 : genRan.RandomInt(1, absX - size - 2); int dz = dir.z == 0 ? 0 : genRan.RandomInt(1, absZ - size - 2); Vec2i dPos = dir * (dx + dz); Vec2i start = build.BoundingWall[wallIndex]; Vec2i cur = start + dPos; //If the building has an entrance, we iterate all window points //to make sure we aren't breaking a door if (build.Entrance != null) { //If we are too close to the door if (cur.QuickDistance(build.Entrance) < (size + 1) * (size + 1)) { if (autoReattempt) { return(AddWindow(genRan, vox, build, wp1, size, height, autoReattempt, reattemptCount)); } return(false); } else { for (int i = 0; i < size; i++) { cur = start + dPos + (dir * i); if (cur.x > 0 && cur.z > 0 && cur.x < build.Width && cur.z < build.Height) { if (vox.GetVoxel(cur.x, 2, cur.z) == Voxel.glass) { if (autoReattempt) { return(AddWindow(genRan, vox, build, wp1, size, height, autoReattempt, reattemptCount)); } return(false); } } else { if (autoReattempt) { return(AddWindow(genRan, vox, build, wp1, size, height, autoReattempt, reattemptCount)); } return(false); } } } } cur = start + dPos; Vector3 position = (cur - dir).AsVector3() + Vector3.up; Vector3 scale = new Vector3(0.5f, 2, (dir.x + dir.z) * size); float rotation = Mathf.Acos(dir.z) * Mathf.Rad2Deg; //GlassWindow window = new GlassWindow(position, scale, rotation); //build.AddObjectReference(window); for (int i = -1; i <= size; i++) { cur = start + dPos + dir * i; for (int y = 1; y <= 2 + height; y++) { if (i < 0 || i >= size || y == 1 || y == 2 + height) { vox.AddVoxel(cur.x, y, cur.z, Voxel.glass); } else { vox.ClearVoxel(cur.x, y, cur.z); vox.SetVoxel(cur.x, y, cur.z, Voxel.glass); } //vox.Add(cur.x, y, cur.z, Voxel.none); } } return(true); }
private void AddInitPaths() { int nodeSize = TileSize / NODE_RES; int xStart = GenerationRandom.RandomInt(0 + 3, nodeSize - 3); int zLen = nodeSize - GenerationRandom.RandomInt(0, 3); //Ends 0-3 chunks from settlement end int zStartWest = GenerationRandom.RandomInt(2, nodeSize - 3); int xLenWest = GenerationRandom.RandomInt(xStart - 2, xStart); EntranceNode = new Vec2i(xStart, 0); //Add the path nodes along the z direction (start at south -> north) for (int z = 0; z < zLen; z++) { PathNodes[xStart, z] = 100; TestNodes2[xStart, z] = new SettlementPathNode(new Vec2i(xStart * NODE_RES, z * NODE_RES)); TestNodes2[xStart, z].IsMain = true; SetTile(xStart * NODE_RES, z * NODE_RES, Tile.TEST_BLUE); } ENTR_NODE = TestNodes2[xStart, 0]; int zStartEast = GenerationRandom.RandomInt(2, nodeSize - 3); int xLenEast = GenerationRandom.RandomInt(nodeSize - xStart - 2, nodeSize - xStart); for (int x = 0; x < xLenEast; x++) { PathNodes[xStart + x, zStartEast] = 100; } for (int x = 0; x < xLenWest; x++) { PathNodes[xStart - x, zStartWest] = 100; } for (int x = 0; x < nodeSize; x++) { for (int z = 0; z < nodeSize; z++) { if (PathNodes[x, z] != 0) { if (x > 0 && PathNodes[x - 1, z] != 0) { SetTiles((x - 1) * NODE_RES, z * NODE_RES - 2, (x) * NODE_RES, z * NODE_RES + 2, Tile.TEST_BLUE); } if (z > 0 && PathNodes[x, z - 1] != 0) { SetTiles((x) * NODE_RES - 2, (z - 1) * NODE_RES, (x) * NODE_RES + 2, z * NODE_RES, Tile.TEST_BLUE); } if (x < nodeSize - 1 && PathNodes[x + 1, z] != 0) { SetTiles((x) * NODE_RES, z * NODE_RES - 2, (x + 1) * NODE_RES, z * NODE_RES + 2, Tile.TEST_BLUE); } if (z < nodeSize - 1 && PathNodes[x, z + 1] != 0) { SetTiles((x) * NODE_RES - 2, (z) * NODE_RES, (x) * NODE_RES + 2, (z + 1) * NODE_RES, Tile.TEST_BLUE); } } } } }
public Vec2i[] ChooseStartChunks(int count, int minSep) { //The angle between map middle that each capital should approximately //be seperated by float thetaSep = 360f / count; float thetaOffset = GenRan.Random(0, 90); Vector2 middle = new Vector2(World.WorldSize / 2, World.WorldSize / 2); Vec2i[] caps = new Vec2i[count]; //We iterate each of the kingdom capital positions we wish to calculate for (int i = 0; i < count; i++) { //Theta compared to (1,0) anticlockwise float theta = thetaOffset + thetaSep * i; float dx = Mathf.Cos(theta * Mathf.Deg2Rad); float dz = Mathf.Sin(theta * Mathf.Deg2Rad); Vector2 delta = new Vector2(dx, dz); //We start at middle Vector2 current = middle; int minLength = 32; int maxLength = -1; //iterate out for (int l = minLength; l < World.WorldSize / 2; l++) { current = middle + delta * l; Vec2i curChunk = Vec2i.FromVector2(current); ChunkBase2 cb = GameGen.TerGen.ChunkBases[curChunk.x, curChunk.z]; if (cb.Biome == ChunkBiome.ocean) { //When we reach the ocean, we define this as the max distance away maxLength = l - 1; break; } } //Capital is random point between min length, and max length Vec2i capPoint = Vec2i.FromVector2(middle + GenRan.RandomInt(minLength, maxLength) * delta); caps[i] = capPoint; } return(caps); Vec2i[] dirs = new Vec2i[] { new Vec2i(1, 1), new Vec2i(-1, 1), new Vec2i(-1, -1), new Vec2i(1, -1) }; Vec2i mid = new Vec2i(World.WorldSize / 2, World.WorldSize / 2); for (int i = 100; i < 400; i++) { for (int j = 0; j < dirs.Length; j++) { if (caps[j] == null) { Vec2i p = mid + dirs[j] * i; ChunkBase2 b = GameGen.TerGen.ChunkBases[p.x, p.z]; if (b.Biome == ChunkBiome.ocean) { Vec2i p_ = mid + dirs[j] * (int)(0.75f * i); caps[j] = p_; GridPoint gp = GameGen.GridPlacement.GetNearestPoint(p_); } } } } return(caps); for (int i = 0; i < count; i++) { bool validPoint = false; while (!validPoint) { Vec2i pos = GenRan.RandomVec2i(0, World.WorldSize - 1); GridPoint p = GameGen.GridPlacement.GetNearestPoint(pos); if (p != null && p.IsValid) { if (i == 0) { caps[0] = p.ChunkPos; validPoint = true; } else { int minDist = -1; for (int j = 0; j < i; j++) { int sqrDist = p.ChunkPos.QuickDistance(caps[j]); if (minDist == -1 || sqrDist < minDist) { minDist = sqrDist; } } if (minDist < minSep * minSep) { caps[i] = p.ChunkPos; validPoint = true; } } } } Debug.Log(caps[i]); } return(caps); }
private void FromRiverSource(Vec2i source, Vec2i end, float startHeight = -1, int distSinceFork = 0, bool riverRaySearch = true) { int i = 0; if (startHeight < 0) { startHeight = ChunkBases[source.x, source.z].Height; } i = 0; Vec2i last = source; Vec2i mainDir = CalcDir(source, end); Vec2i current = source + mainDir; bool isDone = false; Vector2 exactCurrent = current.AsVector2(); List <RiverPoint> river = new List <RiverPoint>(); ChunkRiverNode previousNode = null; while (!isDone) { int distToEnd = end.QuickDistance(current); i++; /* * if(i%16 == 0 && riverRaySearch) * { * bool success = false; * //search up to 16 chunks away * for(int j=1; j<16; j++) * { * if (success) * break; * //search all 8 directions * foreach(Vec2i v in Vec2i.OCT_DIRDIR) * { * Vec2i p = current + v * j; * * if(ChunkBases[p.x,p.z].Biome == ChunkBiome.ocean || ChunkBases[p.x, p.z].ChunkFeature is ChunkRiverNode) * { * end = p; * success = true; * break; * } * } * } * }*/ // Vector2 currentFlow = FlowField[current.x, current.z]; float fx = PerlinNoise(current.x, current.z, 36) * 2 - 1; float fz = PerlinNoise(current.x, current.z, 37) * 2 - 1; Vector2 noiseFlow = new Vector2(fx, fz); Vector2 flowField = FlowField[current.x, current.z]; Vector2 targetFlow = (end - current).AsVector2().normalized; float targetFlowMult = distToEnd < 400 ? 4 * Mathf.Exp((400f - distToEnd) / 200f) : 4; Vector2 flow = (noiseFlow + targetFlowMult * targetFlow + 3.5f * flowField).normalized; exactCurrent += flow; current = Vec2i.FromVector2(exactCurrent); int check = Mathf.Min(river.Count, 5); bool isValid = true; for (int j = 0; j < check; j++) { if (river[river.Count - j - 1].Pos == current) { isValid = false; } } if (!isValid) { current += mainDir; exactCurrent = current.AsVector2(); } if (ChunkBases[current.x, current.z].Biome == ChunkBiome.ocean) { isDone = true; } if (ChunkBases[current.x, current.z].ChunkFeature is ChunkRiverNode) { isDone = true; //Shouldn't be null, but lets do a check anyway if (previousNode != null) { //Get the river node ChunkRiverNode endNode = ChunkBases[current.x, current.z].ChunkFeature as ChunkRiverNode; //Inform river nodes of flow endNode.FlowIn.Add(previousNode); previousNode.FlowOut.Add(endNode); ModifyRiverHeight(endNode); } if (GenRan.Random() < 0.5f) { PlaceLake(current, 8); } } if (current == end) { isDone = true; } ChunkRiverNode nextNode = new ChunkRiverNode(current); ChunkBases[current.x, current.z].SetChunkFeature(nextNode); if (previousNode != null) { nextNode.FlowIn.Add(previousNode); previousNode.FlowOut.Add(nextNode); } previousNode = nextNode; //If this chunk is too high, we modify it and the surrounding area if (ChunkBases[current.x, current.z].Height > startHeight) { ModifyRiverValleyHeight(current, startHeight); } else if (ChunkBases[current.x, current.z].Height < startHeight) { startHeight = ChunkBases[current.x, current.z].Height; } RiverPoint rp = new RiverPoint(); rp.Pos = current; rp.Flow = flow; river.Add(rp); if (i > 4096) { PlaceLake(current, 12); return; } /* * distSinceFork++; * * if(distSinceFork > 256) * { * * float p = Mathf.Exp(-distSinceFork/200f); * if(p < GenRan.Random()) * { * * Vec2i delta = GenRan.RandomVec2i(-64, 64); * * FromRiverSource(current + delta, current); * distSinceFork = 0; * } * * }*/ mainDir = CalcDir(current, end); } return; if (river.Count > 128) { int forkCount = Mathf.CeilToInt(river.Count / 128f); int jump = (int)(((float)river.Count) / forkCount); int index = GenRan.RandomInt(0, jump); for (int j = 0; j < forkCount; j++) { if (index > river.Count - 30) { return; } RiverPoint rp = river[index]; Vec2i forkEnd = rp.Pos; Vec2i delta = GenRan.RandomVec2i(-32, 32); Vector2 endToForkDir = (forkEnd - end).AsVector2().normalized; Vec2i forkStart = Vec2i.FromVector2(forkEnd.AsVector2() + endToForkDir * GenRan.Random(64, 128)) + delta; FromRiverSource(forkStart, forkEnd); index += jump + GenRan.RandomInt(-32, 32); } } }
/// <summary> /// Generates a tunnel of width 1 as a guide to generate the rest of /// the dungeon /// </summary> private void GeneratePath(Vec3i startPosition, int radius = 3) { //We define the list that stores the tunnel List <Vec3i> tunnel = new List <Vec3i>(); //We initiate it with a start position, and a second position (this helps define the initial direction) tunnel.Add(startPosition); //The second position is chosen as the direction away from the boundry of the dungeon int xDir = startPosition.x < TileSize.x / 2 ? 1 : GenRan.RandomInt(-1, 1); int zDir = xDir == 0 ? (startPosition.z < TileSize.x / 2 ? 1 : -1) : GenRan.RandomInt(-1, 2); Vec3i startDirection = new Vec3i(xDir, 0, zDir); tunnel.Add(startPosition + startDirection); startPosition = startPosition + startDirection; for (int i = 0; i < 100; i++) { startPosition = ChooseNextPosition(tunnel, radius); } foreach (Vec3i v in tunnel) { Tunnel(v.x, v.y, v.z, 2); } }