Example #1
0
        /// <summary>
        /// Decides on the bounds for each hall.  Also writes to the adjacent rooms' SideReqs and tile permissions
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="vertical"></param>
        /// <param name="rand">todo: describe rand parameter on ChooseHallBounds</param>
        public void ChooseHallBounds(IRandom rand, int x, int y, bool vertical)
        {
            GridRoomPlan startRoom = this.GetRoomPlan(new Loc(x, y));
            GridRoomPlan endRoom   = this.GetRoomPlan(new Loc(x + (vertical ? 0 : 1), y + (vertical ? 1 : 0)));

            GridHallGroup hallGroup = vertical ? this.VHalls[x][y] : this.HHalls[x][y];

            if (hallGroup.MainHall != null)
            {
                // also sets the sidereqs
                int      tier          = vertical ? x : y;
                Dir4     dir           = vertical ? Dir4.Down : Dir4.Right;
                IntRange startRange    = this.GetHallTouchRange(startRoom.RoomGen, dir, tier);
                IntRange endRange      = this.GetHallTouchRange(endRoom.RoomGen, dir.Reverse(), tier);
                IntRange combinedRange = new IntRange(Math.Min(startRange.Min, endRange.Min), Math.Max(startRange.Max, endRange.Max));
                Loc      start         = startRoom.RoomGen.Draw.End;
                Loc      end           = endRoom.RoomGen.Draw.Start;

                Rect startCell = this.GetCellBounds(startRoom.Bounds);
                Rect endCell   = this.GetCellBounds(endRoom.Bounds);

                Rect bounds = vertical ? new Rect(combinedRange.Min, start.Y, combinedRange.Length, end.Y - start.Y)
                    : new Rect(start.X, combinedRange.Min, end.X - start.X, combinedRange.Length);

                // a side constitutes intruding bound when the rectangle moves forward enough to go to the other side
                // and the other side touched is outside of side B's bound (including borders)

                // startRange intrudes if startRange goes outside end's tier (borders included)
                bool startIntrude = !endCell.GetSide(dir.ToAxis()).Contains(startRange);

                // and end is greater than the edge (borders excluded)
                bool endTouch = bounds.GetScalar(dir) == endCell.GetScalar(dir.Reverse());

                bool endIntrude = !startCell.GetSide(dir.ToAxis()).Contains(endRange);

                // and end is greater than the edge (borders excluded)
                bool startTouch = bounds.GetScalar(dir.Reverse()) == startCell.GetScalar(dir);

                // neither side intrudes bound: use the computed rectangle
                if ((!startIntrude && !endIntrude) || (endTouch && startTouch) ||
                    (!(startIntrude && endIntrude) && ((startIntrude && endTouch) || (endIntrude && startTouch))))
                {
                    hallGroup.MainHall.RoomGen.PrepareSize(rand, bounds.Size);
                    hallGroup.MainHall.RoomGen.SetLoc(bounds.Start);
                }
                else
                {
                    int      divPoint      = startCell.GetScalar(dir) + 1;
                    IntRange startDivRange = startRange;
                    IntRange endDivRange   = endRange;
                    if (startIntrude && !endIntrude)
                    {
                        // side A intrudes bound, side B does not: divide A and B; doesn't matter who gets border
                        // side A touches border, side B does not: divide A and B; A gets border
                        //
                        // side A does not, side B touches border: A gets border; don't need B - this cannot happen
                        // side A touches border, side B touches border: A gets border; don't need B - this cannot happen
                        //
                        // in short, divide with start getting the border
                        // startDivRange needs to contain endRange
                        startDivRange = combinedRange;
                    }
                    else if (!startIntrude && endIntrude)
                    {
                        // side A does not, side B intrudes bound: divide A and B; doesn't matter who gets border
                        // side A does not, side B touches border: divide A and B; B gets border
                        //
                        // side A touches border, side B does not: B gets border; don't need A - this cannot happen
                        // side A touches border, side B touches border: B gets border; don't need B - this cannot happen
                        //
                        // in short, divide with end getting the border
                        // endDivRange needs to contain startRange
                        divPoint    = startCell.GetScalar(dir);
                        endDivRange = combinedRange;
                    }
                    else
                    {
                        // side A intrudes bound, side B intrudes bound: divide A and B; doesn't matter who gets border
                        if (startTouch)
                        {
                            // side A touches border, side B does not: divide A and B; A gets border
                        }

                        if (endTouch)
                        {
                            // side A does not, side B touches border: divide A and B; B gets border
                            divPoint = startCell.GetScalar(dir);
                        }

                        // side A touches border, side B touches border: A gets border; don't need B -  this cannot happen
                        // both sides need to cover the intersection of their cells
                        IntRange interCellSide = IntRange.Intersect(startCell.GetSide(dir.ToAxis()), endCell.GetSide(dir.ToAxis()));
                        startDivRange = IntRange.IncludeRange(startDivRange, interCellSide);
                        endDivRange   = IntRange.IncludeRange(endDivRange, interCellSide);
                    }

                    Rect startBox = vertical ? new Rect(startDivRange.Min, start.Y, startDivRange.Length, divPoint - start.Y)
                        : new Rect(start.X, startDivRange.Min, divPoint - start.X, startDivRange.Length);
                    Rect endBox = vertical ? new Rect(endDivRange.Min, divPoint, endDivRange.Length, end.Y - divPoint)
                        : new Rect(divPoint, endDivRange.Min, end.X - divPoint, endDivRange.Length);

                    GridHallPlan originalHall = hallGroup.MainHall;
                    hallGroup.HallParts.Add(new GridHallPlan((IPermissiveRoomGen)originalHall.RoomGen.Copy(), originalHall.Components));
                    hallGroup.HallParts[0].RoomGen.PrepareSize(rand, startBox.Size);
                    hallGroup.HallParts[0].RoomGen.SetLoc(startBox.Start);
                    hallGroup.HallParts[1].RoomGen.PrepareSize(rand, endBox.Size);
                    hallGroup.HallParts[1].RoomGen.SetLoc(endBox.Start);
                }
            }
        }
Example #2
0
        /// <summary>
        /// Gets the minimum range along the side of a room that includes all of its fulfillable borders.
        /// Special cases arise if the room is multi-cell.
        /// </summary>
        /// <param name="room"></param>
        /// <param name="dir">Direction from room to hall.</param>
        /// <param name="tier"></param>
        /// <returns></returns>
        public virtual IntRange GetHallTouchRange(IRoomGen room, Dir4 dir, int tier)
        {
            bool vertical = dir.ToAxis() == Axis4.Vert;

            // The hall will touch the whole fulfillable side of each room under normal circumstances.
            // Things get tricky for a target room that occupies more than one cell.
            // First, try to cover only the part of the target room that's in the cell.
            int      tierStart  = vertical ? tier * (this.WidthPerCell + this.CellWall) : tier * (this.HeightPerCell + this.CellWall);
            int      tierLength = vertical ? this.WidthPerCell : this.HeightPerCell;
            IntRange tierRange  = new IntRange(tierStart, tierStart + tierLength);
            IntRange roomRange  = room.Draw.GetSide(dir.ToAxis());

            // factor possibletiles into this calculation
            int borderStart = tierStart - roomRange.Min;

            if (borderStart < 0)
            {
                tierRange.Min -= borderStart;
                borderStart    = 0;
            }

            for (int ii = borderStart; ii < roomRange.Length; ii++)
            {
                if (room.GetFulfillableBorder(dir, ii))
                {
                    break;
                }
                else
                {
                    tierRange.Min += 1;
                }
            }

            int borderEnd = tierStart + tierLength - roomRange.Min;

            if (borderEnd > roomRange.Length)
            {
                tierRange.Max += roomRange.Length - borderEnd;
                borderEnd      = roomRange.Length;
            }

            for (int ii = borderEnd - 1; ii >= 0; ii--)
            {
                if (room.GetFulfillableBorder(dir, ii))
                {
                    break;
                }
                else
                {
                    tierRange.Max -= 1;
                }
            }

            if (tierRange.Max > tierRange.Min)
            {
                return(tierRange);
            }

            // If that's not possible, then it means that the room must have fulfillable tiles outside of the current bound.
            // Try to extend the hall until it covers one fulfillable tile of the target room.
            // Easy method: note that the current tierRange range is covering the zone between the tier and the edge of the room (inverted)
            // There will be either a workable range at the start or a workable range at the end, never neither.
            IntRange startRange = new IntRange(tierRange.Max - 1, tierStart + 1);
            IntRange endRange   = new IntRange(tierStart + tierLength - 1, tierRange.Min + 1);

            bool chooseStart = true;
            bool chooseEnd   = true;

            // if tierRanges reached the absolute limits of the roomRange, then there is no fulfillable tile on that side
            if (startRange.Min < roomRange.Min)
            {
                chooseStart = false;
            }
            else if (endRange.Length > startRange.Length)
            {
                chooseEnd = false;
            }

            if (endRange.Max > roomRange.Max)
            {
                chooseEnd = false;
            }
            else if (startRange.Length > endRange.Length)
            {
                chooseStart = false;
            }

            if (!chooseStart && !chooseEnd)
            {
                throw new ArgumentException("PrepareFulfillableBorders did not open at least one open tile for each direction!");
            }

            if (chooseStart)
            {
                return(startRange);
            }
            return(endRange);
        }
Example #3
0
        public override void DrawOnMap(T map)
        {
            // check if there are any sides that have intersections such that straight lines are possible
            var possibleStarts = new Dictionary <Dir4, List <HashSet <int> > >();

            foreach (Dir4 dir in DirExt.VALID_DIR4)
            {
                int scalarStart = this.Draw.Start.GetScalar(dir.ToAxis().Orth());

                // modify the sidereqs: shorten them to accomodate the brush size
                // if the sidereq cannot be shortened further than a width of 1, just use 1
                // the result will have to a degenerate hall, but the important thing is that it will still be functional.
                int             center     = this.Brush.Center.GetScalar(dir.ToAxis().Orth());
                int             width      = this.Brush.Size.GetScalar(dir.ToAxis().Orth());
                List <IntRange> origReqs   = this.RoomSideReqs[dir];
                List <IntRange> moddedReqs = new List <IntRange>();
                foreach (IntRange range in origReqs)
                {
                    IntRange newRange = range;
                    newRange.Min = Math.Min(newRange.Min + center, newRange.Max - 1);
                    newRange.Max = Math.Max(newRange.Max + center + 1 - width, newRange.Min + 1);
                    moddedReqs.Add(newRange);
                }

                possibleStarts[dir] = this.ChoosePossibleStartRanges(map.Rand, scalarStart, this.BorderToFulfill[dir], moddedReqs);
            }

            if ((possibleStarts[Dir4.Down].Count == 0) != (possibleStarts[Dir4.Up].Count == 0) &&
                (possibleStarts[Dir4.Left].Count == 0) != (possibleStarts[Dir4.Right].Count == 0))
            {
                // right angle situation
                // HallTurnBias holds no sway here
                // Get the two directions
                List <Dir4>  dirs      = new List <Dir4>();
                List <int[]> dirStarts = new List <int[]>();
                foreach (Dir4 dir in DirExt.VALID_DIR4)
                {
                    // choose vertical starts if vertical, horiz starts if otherwise
                    if (possibleStarts[dir].Count > 0)
                    {
                        int[] starts = new int[possibleStarts[dir].Count];

                        // choose their start points at random
                        for (int jj = 0; jj < starts.Length; jj++)
                        {
                            starts[jj] = MathUtils.ChooseFromHash(possibleStarts[dir][jj], map.Rand);
                        }

                        dirs.Add(dir);
                        dirStarts.Add(starts);
                    }
                }

                // make the one side extend up to the point where the closest halls would meet if they went straight
                // make the other side extend up to the point where the farthest halls would meet if they went straight
                // flip a coin to see which gets which
                bool extendFar = map.Rand.Next(2) == 0;
                int  minMax1   = GetHallMinMax(dirStarts[1], dirs[0].Reverse(), extendFar);
                this.DrawCombinedHall(map, dirs[0], minMax1, dirStarts[0]);
                int minMax2 = GetHallMinMax(dirStarts[0], dirs[1].Reverse(), !extendFar);
                this.DrawCombinedHall(map, dirs[1], minMax2, dirStarts[1]);

                // TODO: a better way to resolve right-angle multi-halls
                // if there are NO opposite sides of sideReqs at all, we have a right angle situation
                // always connect the closest lines
                // for any additional lines, randomly select one on a side, and choose, up to the current connection point for the other dimension, where to add this line
                // which would then increase the connection point for this dimension.
                // then repeat the process
            }
            else
            {
                bool up    = possibleStarts[Dir4.Up].Count > 0;
                bool down  = possibleStarts[Dir4.Down].Count > 0;
                bool left  = possibleStarts[Dir4.Left].Count > 0;
                bool right = possibleStarts[Dir4.Right].Count > 0;
                bool horiz = left && right;
                bool vert  = down && up;
                if (!horiz && !vert)
                {
                    // if not a right angle situation, and no opposites here, then there's either 0 or 1 direction that the halls are coming from
                    bool hasHall = false;

                    // iterate through to find the one hall direction, and combine all of the halls (if applicable)
                    foreach (Dir4 dir in DirExt.VALID_DIR4)
                    {
                        // choose vertical starts if vertical, horiz starts if otherwise
                        if (possibleStarts[dir].Count > 0)
                        {
                            IntRange side       = this.Draw.GetSide(dir.ToAxis().Orth());
                            int      forwardEnd = map.Rand.Next(side.Min + 1, side.Max - 1);

                            // choose the starts
                            int[] starts = new int[possibleStarts[dir].Count];
                            for (int jj = 0; jj < possibleStarts[dir].Count; jj++)
                            {
                                hasHall    = true;
                                starts[jj] = MathUtils.ChooseFromHash(possibleStarts[dir][jj], map.Rand);
                            }

                            this.DrawCombinedHall(map, dir, forwardEnd, starts);
                        }
                    }

                    // if there is no one hall, throw an error.  this room is MEANT to tie together adjacent rooms
                    if (!hasHall)
                    {
                        throw new ArgumentException("No rooms to connect.");
                    }
                }
                else
                {
                    // 2-way (with opposites) to 4-way intersection
                    bool horizTurn = map.Rand.Next(100) < this.HallTurnBias;
                    bool vertTurn  = map.Rand.Next(100) < this.HallTurnBias;

                    // force a turn if the sides cannot be connected by a straight line
                    // force a straight line if the sides both land on a single aligned tile
                    HashSet <int> horizCross = GetIntersectedTiles(possibleStarts[Dir4.Left], possibleStarts[Dir4.Right]);
                    if (horizCross.Count == 0)
                    {
                        horizTurn = true;
                    }
                    else if (possibleStarts[Dir4.Left].Count == 1 && possibleStarts[Dir4.Right].Count == 1 &&
                             possibleStarts[Dir4.Left][0].Count == 1 && possibleStarts[Dir4.Right][0].Count == 1 &&
                             possibleStarts[Dir4.Left][0].SetEquals(possibleStarts[Dir4.Right][0]))
                    {
                        horizTurn = false;
                    }

                    HashSet <int> vertCross = GetIntersectedTiles(possibleStarts[Dir4.Down], possibleStarts[Dir4.Up]);
                    if (vertCross.Count == 0)
                    {
                        vertTurn = true;
                    }
                    else if (possibleStarts[Dir4.Down].Count == 1 && possibleStarts[Dir4.Up].Count == 1 &&
                             possibleStarts[Dir4.Down][0].Count == 1 && possibleStarts[Dir4.Up][0].Count == 1 &&
                             possibleStarts[Dir4.Down][0].SetEquals(possibleStarts[Dir4.Up][0]))
                    {
                        vertTurn = false;
                    }

                    // in the case where one hall is crossed and another hall isn't, draw the crossed hall first
                    if (horiz && !vert)
                    {
                        this.DrawPrimaryHall(map, horizCross, possibleStarts, false, horizTurn);
                        this.DrawSecondaryHall(map, vertCross, possibleStarts, true, vertTurn);
                    }
                    else if (!horiz && vert)
                    {
                        this.DrawPrimaryHall(map, vertCross, possibleStarts, true, vertTurn);
                        this.DrawSecondaryHall(map, horizCross, possibleStarts, false, horizTurn);
                    }
                    else
                    {
                        // in the case where one hall is straight and another is angled, draw the straight one first
                        // if both are angled, you can draw any first (horiz by default)
                        // if both are straight, you can draw any first (horiz by default)
                        // if horiz is straight and vert is angled, draw horiz first
                        // if vert is straight and horiz is angled, draw vert first
                        if (!vertTurn && horizTurn)
                        {
                            this.DrawPrimaryHall(map, vertCross, possibleStarts, true, vertTurn);
                            this.DrawSecondaryHall(map, horizCross, possibleStarts, false, horizTurn);
                        }
                        else
                        {
                            this.DrawPrimaryHall(map, horizCross, possibleStarts, false, horizTurn);
                            this.DrawSecondaryHall(map, vertCross, possibleStarts, true, vertTurn);
                        }
                    }
                }
            }

            this.SetRoomBorders(map);
        }
Example #4
0
        public static void AddLegalPlacements(SpawnList <Loc> possiblePlacements, FloorPlan floorPlan, RoomHallIndex indexFrom, IRoomGen roomFrom, IRoomGen room, Dir4 expandTo)
        {
            bool vertical = expandTo.ToAxis() == Axis4.Vert;

            // this scaling factor equalizes the chances of long sides vs short sides
            int reverseSideMult = vertical ? roomFrom.Draw.Width * room.Draw.Width : roomFrom.Draw.Height * room.Draw.Height;

            IntRange side = roomFrom.Draw.GetSide(expandTo.ToAxis());

            // subtract the room's original size, not the inflated trialrect size
            side.Min -= (vertical ? room.Draw.Size.X : room.Draw.Size.Y) - 1;

            Rect tryRect = room.Draw;

            // expand in every direction
            // this will create a one-tile buffer to check for collisions
            tryRect.Inflate(1, 1);
            int currentScalar = side.Min;

            while (currentScalar < side.Max)
            {
                // compute the location
                Loc trialLoc = roomFrom.GetEdgeRectLoc(expandTo, room.Draw.Size, currentScalar);
                tryRect.Start = trialLoc + new Loc(-1, -1);

                // check for collisions (not counting the rectangle from)
                List <RoomHallIndex> collisions = floorPlan.CheckCollision(tryRect);

                // find the first tile in which no collisions will be found
                int  maxCollideScalar = currentScalar;
                bool collided         = false;
                foreach (RoomHallIndex collision in collisions)
                {
                    if (collision != indexFrom)
                    {
                        IRoomGen collideRoom = floorPlan.GetRoomHall(collision).RoomGen;

                        // this is the point at which the new room will barely touch the collided room
                        // the +1 at the end will move it into the safe zone
                        maxCollideScalar = Math.Max(maxCollideScalar, vertical ? collideRoom.Draw.Right : collideRoom.Draw.Bottom);
                        collided         = true;
                    }
                }

                // if no collisions were hit, do final checks and add the room
                if (!collided)
                {
                    Loc locTo = roomFrom.GetEdgeRectLoc(expandTo, room.Draw.Size, currentScalar);

                    // must be within the borders of the floor!
                    if (floorPlan.DrawRect.Contains(new Rect(locTo, room.Draw.Size)))
                    {
                        // check the border match and if add to possible placements
                        int chanceTo = FloorPlan.GetBorderMatch(roomFrom, room, locTo, expandTo);
                        if (chanceTo > 0)
                        {
                            possiblePlacements.Add(locTo, chanceTo * reverseSideMult);
                        }
                    }
                }

                currentScalar = maxCollideScalar + 1;
            }
        }
Example #5
0
 void ISpawnRangeDict.SetSpawnRange(object key, IntRange range)
 {
     this.SetSpawnRange((TK)key, range);
 }
Example #6
0
 public SpawnRange(TV item, int rate, IntRange range)
 {
     this.Spawn = item;
     this.Rate  = rate;
     this.Range = range;
 }
Example #7
0
 void ISpawnRangeDict.Add(object key, object spawn, IntRange range, int rate)
 {
     this.Add((TK)key, (TV)spawn, range, rate);
 }
Example #8
0
 public void SetSpawnRange(TK key, IntRange range)
 {
     this.spawns[key] = new SpawnRange(this.spawns[key].Spawn, this.spawns[key].Rate, range);
 }
Example #9
0
 void ISpawnRangeList.Insert(int index, object spawn, IntRange range, int rate)
 {
     this.Insert(index, (T)spawn, range, rate);
 }
Example #10
0
 void ISpawnRangeList.Add(object spawn, IntRange range, int rate)
 {
     this.Add((T)spawn, range, rate);
 }
Example #11
0
 public void SetSpawnRange(int index, IntRange range)
 {
     this.spawns[index] = new SpawnRange(this.spawns[index].Spawn, this.spawns[index].Rate, range);
 }