Ejemplo n.º 1
0
        private bool HasWaterInDirection(
            IHexCell cell, HexDirection direction, IEnumerable <IHexCell> oceanCells
            )
        {
            var neighbor = Grid.GetNeighbor(cell, direction);

            return(neighbor != null && (neighbor.Terrain.IsWater() || oceanCells.Contains(neighbor)));
        }
Ejemplo n.º 2
0
        //The fact that we're building sections only from the NE, E, and SE
        //cell edges is very important. A lot of the code here is structured
        //as it is to deal with this reality.
        public void RefreshRiverSections()
        {
            sections.Clear();
            SectionBetweenCells.Clear();

            CellEdgeContourCanon.Clear();

            foreach (var cell in Grid.Cells)
            {
                for (HexDirection direction = HexDirection.NE; direction <= HexDirection.SE; direction++)
                {
                    if (RiverCanon.HasRiverAlongEdge(cell, direction))
                    {
                        var neighbor = Grid.GetNeighbor(cell, direction);

                        Vector3 start = cell.AbsolutePosition + RenderConfig.GetFirstCorner(direction);
                        Vector3 end   = cell.AbsolutePosition + RenderConfig.GetSecondCorner(direction);

                        RiverFlow flow = RiverCanon.GetFlowOfRiverAtEdge(cell, direction);

                        //Our control points need to operate differently if we're at endpoints,
                        //and both ControlOne and ControlTwo might have alternate behavior.
                        //We need to check both ends of the section for endpoints
                        bool previousCellRiver     = RiverCanon.HasRiverAlongEdge(cell, direction.Previous());
                        bool previousNeighborRiver = RiverCanon.HasRiverAlongEdge(neighbor, direction.Previous2());

                        bool hasPreviousEndpoint =
                            !previousCellRiver &&
                            !previousNeighborRiver;

                        bool hasNextEndpoint =
                            !RiverCanon.HasRiverAlongEdge(cell, direction.Next()) &&
                            !RiverCanon.HasRiverAlongEdge(neighbor, direction.Next2());

                        var newRiverSection = new RiverSection()
                        {
                            AdjacentCellOne  = cell,
                            AdjacentCellTwo  = neighbor,
                            DirectionFromOne = direction,
                            Start            = start,
                            End                 = end,
                            FlowFromOne         = flow,
                            HasPreviousEndpoint = hasPreviousEndpoint,
                            HasNextEndpoint     = hasNextEndpoint
                        };

                        SectionBetweenCells[new Tuple <IHexCell, IHexCell>(cell, neighbor)] = newRiverSection;

                        sections.Add(newRiverSection);
                    }
                }
            }
        }
        public void TriangulateRoads(IHexCell cell, IHexMesh roadsMesh)
        {
            if (cell.HasRoads)
            {
                var cellHash = NoiseGenerator.SampleHashGrid(cell.AbsolutePositionXZ);

                var directionsWithRoad = EnumUtil.GetValues <HexDirection>().Where(
                    direction => Grid.HasNeighbor(cell, direction) && Grid.GetNeighbor(cell, direction).HasRoads
                    ).ToList();

                while (directionsWithRoad.Any())
                {
                    var startDirection = directionsWithRoad.First();

                    directionsWithRoad.Remove(startDirection);

                    BezierSpline spline;

                    if (directionsWithRoad.Any())
                    {
                        var endDirection = GetBestDirection(startDirection, directionsWithRoad, cellHash);

                        directionsWithRoad.Remove(endDirection);

                        spline = BuildSplineBetween(cell, startDirection, endDirection);
                    }
                    else
                    {
                        spline = BuildSplineToCenter(cell, startDirection);
                    }

                    RenderSpline(spline, roadsMesh);
                }
            }
        }
        public void TriangulateMarshes(IHexCell center, IHexMesh mesh)
        {
            foreach (var direction in EnumUtil.GetValues <HexDirection>())
            {
                if (center.Vegetation == CellVegetation.Marsh)
                {
                    TriangulateMarshCenter(center, direction, mesh);
                }

                if (direction > HexDirection.SE)
                {
                    continue;
                }

                IHexCell right = Grid.GetNeighbor(center, direction);
                IHexCell left  = Grid.GetNeighbor(center, direction.Previous());

                if (right == null)
                {
                    continue;
                }

                float centerV = center.Vegetation == CellVegetation.Marsh ? 1f : 0f;
                float leftV   = left != null && left.Vegetation == CellVegetation.Marsh ? 1f : 0f;
                float rightV  = right != null && right.Vegetation == CellVegetation.Marsh ? 1f : 0f;

                if (centerV != 0f || rightV != 0f)
                {
                    TriangulateMarshEdge(center, right, centerV, rightV, direction, mesh);
                }

                if (direction > HexDirection.E)
                {
                    continue;
                }

                if (left == null)
                {
                    continue;
                }

                if (centerV != 0f || leftV != 0f || rightV != 0f)
                {
                    TriangulateMarshCorner(center, left, right, centerV, leftV, rightV, direction, mesh);
                }
            }
        }
Ejemplo n.º 5
0
        public void TriangulateContoursBetween(
            IHexCell center, IHexCell right, HexDirection direction, Color centerWeights, Color rightWeights, IHexMesh mesh
            )
        {
            var centerRightContour = CellContourCanon.GetContourForCellEdge(center, direction);
            var rightCenterContour = CellContourCanon.GetContourForCellEdge(right, direction.Opposite());

            int centerRightIndex = 1, rightCenterIndex = rightCenterContour.Count - 1;

            while (centerRightIndex < centerRightContour.Count && rightCenterIndex > 0)
            {
                mesh.AddQuad(
                    centerRightContour[centerRightIndex - 1].ToXYZ(), centerRightContour[centerRightIndex].ToXYZ(),
                    rightCenterContour[rightCenterIndex].ToXYZ(), rightCenterContour[rightCenterIndex - 1].ToXYZ()
                    );

                mesh.AddQuadColor(centerWeights, rightWeights);

                centerRightIndex++;
                rightCenterIndex--;
            }

            for (; centerRightIndex < centerRightContour.Count; centerRightIndex++)
            {
                mesh.AddTriangle(
                    centerRightContour[centerRightIndex - 1].ToXYZ(), rightCenterContour[0].ToXYZ(), centerRightContour[centerRightIndex].ToXYZ()
                    );

                mesh.AddTriangleColor(centerWeights, rightWeights, centerWeights);
            }

            for (; rightCenterIndex > 0; rightCenterIndex--)
            {
                mesh.AddTriangle(
                    centerRightContour.Last().ToXYZ(), rightCenterContour[rightCenterIndex].ToXYZ(), rightCenterContour[rightCenterIndex - 1].ToXYZ()
                    );

                mesh.AddTriangleColor(centerWeights, rightWeights, rightWeights);
            }

            if (RiverCanon.HasRiverAlongEdge(right, direction.Next2()))
            {
                var nextRight = Grid.GetNeighbor(center, direction.Next());

                var rightNextRightContour = CellContourCanon.GetContourForCellEdge(right, direction.Next2());
                var nextRightRightContour = CellContourCanon.GetContourForCellEdge(nextRight, direction.Previous());

                mesh.AddTriangle(
                    centerRightContour.Last().ToXYZ(),
                    rightNextRightContour.Last().ToXYZ(),
                    nextRightRightContour.First().ToXYZ()
                    );

                mesh.AddTriangleColor(centerWeights, rightWeights, rightWeights);
            }
        }
        public void RationalizeRiverContoursInCorner(IHexCell center, HexDirection direction)
        {
            IHexCell left = Grid.GetNeighbor(center, direction.Previous());

            //Contours will only fall short or overlap when we're at a river confluence,
            //so we can save ourselves time by checking for that
            if (left != null &&
                RiverCanon.HasRiverAlongEdge(center, direction.Previous()) &&
                RiverCanon.HasRiverAlongEdge(center, direction) &&
                RiverCanon.HasRiverAlongEdge(left, direction.Next())
                )
            {
                IHexCell right = Grid.GetNeighbor(center, direction);

                RiverSection centerLeftSection  = RiverSectionCanon.GetSectionBetweenCells(center, left);
                RiverSection centerRightSection = RiverSectionCanon.GetSectionBetweenCells(center, right);

                bool shouldCullContours = true;

                //Rationalizing contours also doesn't need to occur between segments
                //that are adjacent segments of the same river, which would put us on
                //the inside of a river curve. So we make some checks to exclude that
                //possibility, as well
                foreach (var river in RiverAssemblyCanon.Rivers)
                {
                    int leftIndex  = river.IndexOf(centerLeftSection);
                    int rightIndex = river.IndexOf(centerRightSection);

                    bool areAdjacentInRiver = ((leftIndex + 1) == rightIndex) || ((rightIndex + 1) == leftIndex);

                    if (leftIndex != -1 && rightIndex != -1 && areAdjacentInRiver)
                    {
                        shouldCullContours = false;
                        break;
                    }
                }

                if (shouldCullContours)
                {
                    ContourRationalizer.RationalizeCellContours(center, direction);
                }
            }
        }
Ejemplo n.º 7
0
        public void TriangulateCultureInDirection(
            IHexCell center, HexDirection direction, IHexMesh cultureMesh
            )
        {
            var centerOwner = CivTerritoryLogic.GetCivClaimingCell(center);

            if (centerOwner == null)
            {
                return;
            }

            IHexCell left      = Grid.GetNeighbor(center, direction.Previous());
            IHexCell right     = Grid.GetNeighbor(center, direction);
            IHexCell nextRight = Grid.GetNeighbor(center, direction.Next());

            if (right == null)
            {
                return;
            }

            if (CivTerritoryLogic.GetCivClaimingCell(right) != centerOwner)
            {
                TriangulateCultureAlongContour(center, direction, centerOwner.Template.Color, cultureMesh);
            }
            else
            {
                var centerRightContour = CellEdgeContourCanon.GetContourForCellEdge(center, direction);
                var rightCenterContour = CellEdgeContourCanon.GetContourForCellEdge(right, direction.Opposite());

                if (rightCenterContour.Count <= 3)
                {
                    TriangulateCultureCorners_FlatEdge(
                        center, left, right, nextRight, direction, centerOwner, centerRightContour, rightCenterContour, cultureMesh
                        );
                }
                else
                {
                    TriangulateCultureCorners_River(
                        center, left, right, nextRight, direction, centerOwner, centerRightContour, cultureMesh
                        );
                }
            }
        }
Ejemplo n.º 8
0
        public void GetOrientationDataFromColors(
            PointOrientationData dataToUse, byte[] indexBytes, Color32 orientationColor, Color weightsColor, Color duckColor
            )
        {
            indexBytes[0] = orientationColor.r;
            indexBytes[1] = orientationColor.g;

            int index = BitConverter.ToInt16(indexBytes, 0) - 1;

            var center = index >= 0 && index < Grid.Cells.Count ? Grid.Cells[index] : null;

            HexDirection sextant = (HexDirection)orientationColor.b;

            if (center != null)
            {
                dataToUse.IsOnGrid = true;

                dataToUse.Center    = center;
                dataToUse.Left      = Grid.GetNeighbor(center, sextant.Previous());
                dataToUse.Right     = Grid.GetNeighbor(center, sextant);
                dataToUse.NextRight = Grid.GetNeighbor(center, sextant.Next());

                dataToUse.CenterWeight    = weightsColor.r;
                dataToUse.LeftWeight      = weightsColor.g;
                dataToUse.RightWeight     = weightsColor.b;
                dataToUse.NextRightWeight = weightsColor.a;

                float weightSum = weightsColor.r + weightsColor.g + weightsColor.b + weightsColor.a;

                dataToUse.RiverWeight = Mathf.Clamp01(1f - weightSum);

                dataToUse.ElevationDuck = duckColor.r;
            }
            else
            {
                dataToUse.Clear();
            }
        }
Ejemplo n.º 9
0
        public void TriangulateCellWeights(IHexCell center, IHexMesh weightsMesh)
        {
            foreach (var direction in EnumUtil.GetValues <HexDirection>())
            {
                bool hasCenterRightRiver = RiverCanon.HasRiverAlongEdge(center, direction);

                IHexCell right = Grid.GetNeighbor(center, direction);

                if (hasCenterRightRiver)
                {
                    TriangulateCellWeights_River(center, right, direction, hasCenterRightRiver, weightsMesh);
                }
                else
                {
                    TriangulateCellWeights_NoRiver(center, right, direction, weightsMesh);
                }
            }
        }
Ejemplo n.º 10
0
        private List <List <IHexCell> > AssembleFarmBlobs()
        {
            var retval = new List <List <IHexCell> >();

            var unassignedFarms = new HashSet <IHexCell>(Grid.Cells.Where(FarmCellFilter));

            var candidateQueue = new Queue <IHexCell>();

            while (unassignedFarms.Count > 0)
            {
                var blobCenter = unassignedFarms.First();

                var newBlob = new List <IHexCell>();

                candidateQueue.Enqueue(blobCenter);

                while (candidateQueue.Count > 0)
                {
                    var activeFarm = candidateQueue.Dequeue();

                    newBlob.Add(activeFarm);

                    unassignedFarms.Remove(activeFarm);

                    foreach (var direction in EnumUtil.GetValues <HexDirection>())
                    {
                        var neighbor = Grid.GetNeighbor(activeFarm, direction);

                        if (unassignedFarms.Contains(neighbor) && BlobExpandFilter(activeFarm, neighbor, direction))
                        {
                            candidateQueue.Enqueue(neighbor);
                        }
                    }
                }

                retval.Add(newBlob);
            }

            return(retval);
        }
Ejemplo n.º 11
0
        public void AddRiverToCell(IHexCell cell, HexDirection edge, RiverFlow flow)
        {
            if (!CanAddRiverToCell(cell, edge, flow))
            {
                throw new InvalidOperationException("CanAddRiverToCell must return true on the given arguments");
            }

            GetPresenceArray(cell)[(int)edge]  = true;
            GetDirectionArray(cell)[(int)edge] = flow;

            var neighborAtEdge = Grid.GetNeighbor(cell, edge);

            if (neighborAtEdge != null)
            {
                GetPresenceArray(neighborAtEdge)[(int)(edge.Opposite())]  = true;
                GetDirectionArray(neighborAtEdge)[(int)(edge.Opposite())] = flow.Opposite();

                CellSignals.GainedRiveredEdge.OnNext(neighborAtEdge);
            }

            CellSignals.GainedRiveredEdge.OnNext(cell);
        }
        public void TriangulateOrientation(IHexCell center, IHexMesh orientationMesh)
        {
            short indexOffset = (short)(center.Index + 1);

            foreach (var direction in EnumUtil.GetValues <HexDirection>())
            {
                byte[] rg = BitConverter.GetBytes(indexOffset);
                byte   b  = (byte)direction;

                var cellColor = new Color32(rg[0], rg[1], b, 0);

                var centerContour = CellContourCanon.GetContourForCellEdge(center, direction);

                for (int i = 1; i < centerContour.Count; i++)
                {
                    orientationMesh.AddTriangle(
                        center.AbsolutePosition, centerContour[i - 1].ToXYZ(), centerContour[i].ToXYZ()
                        );

                    orientationMesh.AddTriangleColor(cellColor);
                }

                if (direction <= HexDirection.SE && RiverCanon.HasRiverAlongEdge(center, direction))
                {
                    var right = Grid.GetNeighbor(center, direction);

                    ContourTriangulator.TriangulateContoursBetween(
                        center, right, direction, cellColor, cellColor, orientationMesh
                        );

                    if (direction <= HexDirection.E && RiverCanon.HasRiverAlongEdge(right, direction.Previous2()))
                    {
                    }
                }
            }
        }
        public RiverSection GetNextActiveSectionForRiver(
            RiverSection activeSection, RiverSection lastSection, HashSet <RiverSection> unassignedSections
            )
        {
            IHexCell center    = activeSection.AdjacentCellOne;
            IHexCell left      = Grid.GetNeighbor(activeSection.AdjacentCellOne, activeSection.DirectionFromOne.Previous());
            IHexCell right     = activeSection.AdjacentCellTwo;
            IHexCell nextRight = Grid.GetNeighbor(activeSection.AdjacentCellOne, activeSection.DirectionFromOne.Next());

            RiverSection centerLeftSection = SectionCanon.GetSectionBetweenCells(center, left);

            //For a section to be a valid next-step, it must be non-null and unassigned.
            //But we also don't want to grab a river adjacent to the previous river,
            //since this could cause weird behavior (go up a river, and then immediately
            //back down it). We thus forbid the acquisition of such rivers.
            //We also need to take flow into account, since it's not valid for a river
            //to suddenly flip its direction
            if (centerLeftSection != null &&
                unassignedSections.Contains(centerLeftSection) &&
                !SectionCanon.IsNeighborOf(centerLeftSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, centerLeftSection)
                )
            {
                return(centerLeftSection);
            }

            RiverSection leftRightSection = SectionCanon.GetSectionBetweenCells(left, right);

            if (leftRightSection != null &&
                unassignedSections.Contains(leftRightSection) &&
                !SectionCanon.IsNeighborOf(leftRightSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, leftRightSection)
                )
            {
                return(leftRightSection);
            }

            RiverSection centerNextRightSection = SectionCanon.GetSectionBetweenCells(center, nextRight);

            if (centerNextRightSection != null &&
                unassignedSections.Contains(centerNextRightSection) &&
                !SectionCanon.IsNeighborOf(centerNextRightSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, centerNextRightSection)
                )
            {
                return(centerNextRightSection);
            }

            RiverSection rightNextRightSection = SectionCanon.GetSectionBetweenCells(right, nextRight);

            if (rightNextRightSection != null &&
                unassignedSections.Contains(rightNextRightSection) &&
                !SectionCanon.IsNeighborOf(rightNextRightSection, lastSection) &&
                SectionCanon.AreSectionFlowsCongruous(activeSection, rightNextRightSection)
                )
            {
                return(rightNextRightSection);
            }

            return(null);
        }
Ejemplo n.º 14
0
        //Center should always be a water cell of some variety.
        //We need to extend water borders into land cells to make
        //sure the water goes all the way up to the shore.
        public void TriangulateWaterForCell(IHexCell center, Transform localTransform, IHexMesh mesh)
        {
            Vector3 localCenterPos = localTransform.InverseTransformPoint(center.AbsolutePosition);

            localCenterPos.y = RenderConfig.WaterY;

            foreach (var direction in EnumUtil.GetValues <HexDirection>())
            {
                Color centerColor, leftColor, rightColor;

                GetWaterColors(center, direction, out centerColor, out leftColor, out rightColor);


                TriangulateWaterCenter(center, mesh, direction, localCenterPos, centerColor);


                var right = Grid.GetNeighbor(center, direction);

                if (right == null || (right.Terrain.IsWater() && direction > HexDirection.SE))
                {
                    continue;
                }

                Vector3 localRightPos = localTransform.InverseTransformPoint(right.AbsolutePosition);
                localRightPos.y = RenderConfig.WaterY;

                if (center.Terrain.IsWater())
                {
                    TriangulateWaterEdge(
                        center, localCenterPos, centerColor, right, localRightPos, rightColor,
                        direction, mesh
                        );
                }
                else if (right.Terrain.IsWater())
                {
                    TriangulateWaterEdge(
                        right, localRightPos, rightColor, center, localCenterPos, centerColor,
                        direction.Opposite(), mesh
                        );
                }


                var left = Grid.GetNeighbor(center, direction.Previous());

                //We can't use the normal paradigm for corners because
                //we aren't triangulating from every cell. We need to
                //triangulate every land corner, and then exclude water
                //corners, which this check does.
                if (left == null ||
                    (right.Terrain.IsWater() && direction > HexDirection.E) ||
                    (left.Terrain.IsWater() && direction > HexDirection.SW)
                    )
                {
                    continue;
                }

                Vector3 localLeftPos = localTransform.InverseTransformPoint(left.AbsolutePosition);
                localLeftPos.y = RenderConfig.WaterY;

                TriangulateWaterCorner(
                    center, localCenterPos, centerColor,
                    right, localRightPos, rightColor,
                    left, localLeftPos, leftColor,
                    direction, mesh
                    );
            }
        }
        public void BuildNonRiverContour(IHexCell center, HexDirection direction)
        {
            if (RiverCanon.HasRiverAlongEdge(center, direction))
            {
                return;
            }

            var contourPoints = new List <Vector2>();

            IHexCell left      = Grid.GetNeighbor(center, direction.Previous());
            IHexCell right     = Grid.GetNeighbor(center, direction);
            IHexCell nextRight = Grid.GetNeighbor(center, direction.Next());

            bool hasCenterLeftRiver      = RiverCanon.HasRiverAlongEdge(center, direction.Previous());
            bool hasCenterNextRightRiver = RiverCanon.HasRiverAlongEdge(center, direction.Next());

            bool hasLeftRightRiver      = left != null && RiverCanon.HasRiverAlongEdge(left, direction.Next());
            bool hasNextRightRightRiver = nextRight != null && RiverCanon.HasRiverAlongEdge(nextRight, direction.Previous());

            if (hasCenterLeftRiver)
            {
                ICollection <Vector2> centerLeftContour = CellEdgeContourCanon.GetContourForCellEdge(center, direction.Previous());

                contourPoints.Add(centerLeftContour.Last());
            }
            else if (hasLeftRightRiver)
            {
                ICollection <Vector2> rightLeftContour = CellEdgeContourCanon.GetContourForCellEdge(right, direction.Previous2());

                contourPoints.Add(rightLeftContour.First());
            }
            else
            {
                contourPoints.Add(center.AbsolutePositionXZ + RenderConfig.GetFirstCornerXZ(direction));
            }

            if (hasCenterNextRightRiver ||
                (hasNextRightRightRiver && RiverCanon.GetFlowOfRiverAtEdge(nextRight, direction.Previous()) == RiverFlow.Clockwise)
                )
            {
                ICollection <Vector2> centerNextRightContour = CellEdgeContourCanon.GetContourForCellEdge(center, direction.Next());

                contourPoints.Add(centerNextRightContour.First());
            }
            else if (hasNextRightRightRiver)
            {
                //We need to add two points here in case the edge is up against the
                //outflow of a river. We've decided to give the outflow over entirely
                //to one of the cells, but other constructions are possible.
                ICollection <Vector2> rightNextRightContour = CellEdgeContourCanon.GetContourForCellEdge(right, direction.Next2());
                ICollection <Vector2> nextRightRightContour = CellEdgeContourCanon.GetContourForCellEdge(nextRight, direction.Previous());

                contourPoints.Add(rightNextRightContour.Last());
                contourPoints.Add(nextRightRightContour.First());
            }
            else
            {
                contourPoints.Add(center.AbsolutePositionXZ + RenderConfig.GetSecondCornerXZ(direction));
            }

            CellEdgeContourCanon.SetContourForCellEdge(center, direction, contourPoints);
        }
Ejemplo n.º 16
0
        private RiverPathResults CreateRiverEndpoint(
            IHexCell previousCell, IHexCell endingCell, IEnumerable <IHexCell> oceanCells,
            HashSet <IHexCell> cellsAdjacentToRiver
            )
        {
            //It's possible (though unlikely) that our endpointwd is a water cell. If
            //it is, we don't need to do anything.e
            if (IsWater(endingCell, oceanCells))
            {
                return(RiverPathResults.Water);
            }

            //If it's not water, we next attempt to connect it to any rivers on the cell
            //or to nearby cells that are submerged in water. We can check and handle
            //both cases at the same time.
            var directionToEnd      = GetDirectionOfNeighbor(previousCell, endingCell);
            var directionToPrevious = directionToEnd.Opposite();

            var bottomLeftNeighbor     = Grid.GetNeighbor(endingCell, directionToEnd.Previous2());
            var topLeftNeighbor        = Grid.GetNeighbor(endingCell, directionToEnd.Previous());
            var straightAcrossNeighbor = Grid.GetNeighbor(endingCell, directionToEnd);
            var topRightNeighbor       = Grid.GetNeighbor(endingCell, directionToEnd.Next());
            var bottomRightNeighbor    = Grid.GetNeighbor(endingCell, directionToEnd.Next2());

            //Case triggers when there's a river or water along the bottom-left edge of
            //endingCell. We can treat it like a sharp CCW turn towards
            //the cell below and to the left of endingCell
            if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Previous2()) ||
                IsWater(bottomLeftNeighbor, oceanCells)
                )
            {
                return(CreateRiverAlongCell_SharpCCWTurn(
                           previousCell, endingCell, bottomLeftNeighbor,
                           directionToPrevious, directionToEnd.Previous2(), oceanCells, cellsAdjacentToRiver
                           ));
            }

            //Case triggers when there's a river or water along the bottom-right edge of
            //endingCell. We can treat it like a sharp CW
            //turn towards the cell below and to the right of endingCell
            if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Next2()) ||
                IsWater(bottomRightNeighbor, oceanCells)
                )
            {
                return(CreateRiverAlongCell_SharpCWTurn(
                           previousCell, endingCell, bottomRightNeighbor,
                           directionToPrevious, directionToEnd.Next2(), oceanCells, cellsAdjacentToRiver
                           ));
            }

            //Case triggers when there's a river or water along the top-left edge of
            //endingCell. In this case, we can treat this like a gentle CCW
            //turn towards the cell above and to the left of endingCell
            if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Previous()) ||
                IsWater(topLeftNeighbor, oceanCells)
                )
            {
                return(CreateRiverAlongCell_GentleCCWTurn(
                           previousCell, endingCell, topLeftNeighbor,
                           directionToPrevious, directionToEnd.Previous(), oceanCells, cellsAdjacentToRiver
                           ));
            }

            //Case triggers when there's a river or water along the top-right edge of
            //endingCell. We can treat it like a gentle CW
            //turn towards the cell above and to the right of endingCell
            if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd.Next()) ||
                IsWater(topRightNeighbor, oceanCells)
                )
            {
                return(CreateRiverAlongCell_GentleCWTurn(
                           previousCell, endingCell, topRightNeighbor,
                           directionToPrevious, directionToEnd.Next(), oceanCells, cellsAdjacentToRiver
                           ));
            }

            //Case triggers when there's a river or water along the top edge of
            //endingCell. In this case, we can treat this like a
            //straight-across section towards the cell above endingCell
            if (RiverCanon.HasRiverAlongEdge(endingCell, directionToEnd) ||
                IsWater(straightAcrossNeighbor, oceanCells)
                )
            {
                return(CreateRiverAlongCell_StraightAcross(
                           previousCell, endingCell, straightAcrossNeighbor,
                           directionToPrevious, directionToEnd, oceanCells, cellsAdjacentToRiver
                           ));
            }

            return(RiverPathResults.Fail);
        }