/// <summary> /// find river path from given starting point on restricted area /// forceRiver = generates river even on not very suitable terrain /// used when generating connecting river /// </summary> public RiverInfo GetRiverFrom(Vertex start, List<Direction> reachedSides, Area restrictedArea, RiverInfo ignoreRiver, int gridStep, bool forceRiver) { float step = Math.Abs(start.height); //height can be negative int x_min = restrictedArea.botLeft.x; int z_min = restrictedArea.botLeft.z; int x_max = restrictedArea.topRight.x; int z_max = restrictedArea.topRight.z; if (step <= 0.1f) //step can't be too small step = 0.1f; else if (step > 0.1f) //step can't be too big step = 0.1f; Vertex highestPoint = ftm.GetHighestpoint(x_min, z_min, x_max, z_max); float maxThreshold = highestPoint.height; //highest value on map - river can't flow that heigh List<FloodNode> reachableNodes = new List<FloodNode>(); reachableNodes.Add(new FloodNode(start, 0)); float threshold = start.height + step; int borderOffset = gridStep + 5; Direction reachedSide = Direction.none; int finalIndex = 0;//index of final node (reached one of the sides) while (reachedSide == Direction.none) { for (int i = 0; i < reachableNodes.Count; i++) { FloodNode currentNode = reachableNodes[i]; if (!currentNode.processed) { int x = (int)currentNode.vertex.x; int z = (int)currentNode.vertex.z; if (rg.IsRiverDefined(x,z) && rg.GetRiverOn(x, z) != ignoreRiver && !ignoreRiver.riverPath.Contains(currentNode.vertex) && !reachedSides.Contains(Direction.river)) { reachedSide = Direction.river; currentNode.vertex.side = Direction.river; finalIndex = i; break; } reachedSide = fmc.GetReachedSide(x, z, borderOffset, x_min, z_min, x_max, z_max); if (reachedSide != Direction.none) { //check if node is not on side that has already been reached bool reachedAvailableSide = fmc.ReachedAvailableSide(x, z, reachedSides, borderOffset, x_min, z_min, x_max, z_max); if (reachedAvailableSide) { finalIndex = i; currentNode.vertex.side = reachedSide; break; } else { reachedSide = Direction.none; } if (reachedSide != Direction.none) { Debug.Log("????"); break; } } if (reachedSide != Direction.none) { break; } //dont process already processed nodes again if (!currentNode.processed) { if (i > terrainWidth* terrainHeight) { Debug.Log("FAIL"); errorRiver.errorMessage = "FAIL - algorithm timeout"; return errorRiver; } List<Vertex> neighbours = ftm.GetGlobal8Neighbours(currentNode.vertex, gridStep, 0, threshold, x_min, x_max, z_min, z_max); if (neighbours.Count == 8) { currentNode.processed = true; } foreach (Vertex v in neighbours) { if (v.height < threshold && !rg.IsRiverDefined(v.x, v.z) && !reachableNodes.Contains(new FloodNode(v, i)) || //normal node !reachedSides.Contains(Direction.river) && rg.IsRiverDefined(v.x, v.z) && //river node !ignoreRiver.riverPath.Contains(v)) { reachableNodes.Add(new FloodNode(v, i)); } } } } if (reachedSide != Direction.none) { break; } } if (reachedSide != Direction.none) { break; } threshold += step; if(!forceRiver && threshold > rg.riverLevel + 2 * step) { Debug.Log("threshold too high: " + threshold); errorRiver.errorMessage = "RIVER FAIL\n reached max threshold: " + threshold; return errorRiver; } if (threshold > maxThreshold) { Debug.Log("step=" + step); Debug.Log("max=" + maxThreshold); errorRiver.errorMessage = "RIVER FAIL\n reached max threshold: " + threshold; return errorRiver; } } int pathIndex = finalIndex; List<Vertex> finalPath = new List<Vertex>(); if (reachedSide == Direction.river) { Vertex lastVertex = reachableNodes[pathIndex].vertex; reachableNodes[pathIndex].vertex.side = Direction.none; finalPath.Add(rg.GetRiverOn(lastVertex.x, lastVertex.z).GetClosestVertexTo(lastVertex)); Debug.Log("added river node: " + finalPath[0]); finalPath[0].side = Direction.river; } else { finalPath.Add(fmc.GetVertexOnBorder(reachableNodes[finalIndex].vertex, borderOffset, reachedSide, x_min, x_max, z_min, z_max)); //add new node which lies exactly on border } RiverInfo river = new RiverInfo(rg); while (pathIndex != 0)//recursively add all vertices of found path { finalPath.Add(reachableNodes[pathIndex].vertex); pathIndex = reachableNodes[pathIndex].parentIndex; river.UpdateLowestPoint(reachableNodes[pathIndex].vertex); } finalPath.Add(start); //if added border node is too close to next node, delete the next one if (finalPath.Count > 1 && Vector3.Distance(finalPath[0], finalPath[1]) < gridStep / 2 && finalPath[1] != start) { finalPath.RemoveAt(1); } finalPath.Reverse(); river.riverPath = finalPath; river.gridStep = gridStep; reachedSides.Add(reachedSide); return river; }