/// <summary> /// Rolls the dice, returning the sum. /// </summary> /// <param name="rng">The RNG to use for rolling,</param> /// <returns>The sum of the roll.</returns> public int GetResult(IEnhancedRandom rng) { _diceResults.Clear(); var sum = 0; LastMultiplicity = Multiplicity.GetResult(rng); LastSidedness = Sides.GetResult(rng); if (LastMultiplicity < 0) { throw new InvalidMultiplicityException(); } if (LastSidedness <= 0) { throw new ImpossibleDieException(); } for (var i = 0; i < LastMultiplicity; i++) { var diceVal = rng.NextInt(1, LastSidedness + 1); sum += diceVal; _diceResults.Add(diceVal); } return(sum); }
private static int GetDirectionIndex(bool[] validDirections, IEnhancedRandom rng) { // 10 tries to find random ok valid var randomSuccess = false; var tempDirectionIndex = 0; for (var randomCounter = 0; randomCounter < 10; randomCounter++) { tempDirectionIndex = rng.NextInt(4); if (!validDirections[tempDirectionIndex]) { continue; } randomSuccess = true; break; } if (randomSuccess) { return(tempDirectionIndex); } // Couldn't find an active valid, so just run through each one if (validDirections[0]) { tempDirectionIndex = 0; } else if (validDirections[1]) { tempDirectionIndex = 1; } else if (validDirections[2]) { tempDirectionIndex = 2; } else { tempDirectionIndex = 3; } return(tempDirectionIndex); }
/// <inheritdoc /> protected override IEnumerator <object?> OnPerform(GenerationContext context) { // Validate configuration if (MinRooms > MaxRooms) { throw new InvalidConfigurationException(this, nameof(MinRooms), $"The value must be less than or equal to the value of {nameof(MaxRooms)}."); } if (RoomMinSize > RoomMaxSize) { throw new InvalidConfigurationException(this, nameof(RoomMinSize), $"The value must be less than or equal to the value of ${nameof(RoomMaxSize)}."); } if (RoomSizeRatioX <= 0f) { throw new InvalidConfigurationException(this, nameof(RoomSizeRatioX), "The value must be greater than 0."); } if (RoomSizeRatioY <= 0f) { throw new InvalidConfigurationException(this, nameof(RoomSizeRatioY), "The value must be greater than 0."); } // Get or create/add a wall-floor context component var wallFloorContext = context.GetFirstOrNew <ISettableGridView <bool> >( () => new ArrayView <bool>(context.Width, context.Height), WallFloorComponentTag ); // Determine how many rooms to generate var roomCounter = RNG.NextInt(MinRooms, MaxRooms + 1); // Get or create/add a rooms context component var roomsContext = context.GetFirstOrNew( () => new ItemList <Rectangle>(roomCounter), RoomsComponentTag ); // Try to place all the rooms while (roomCounter != 0) { var tryCounterCreate = MaxCreationAttempts; var placed = false; // Attempt to create the room until either we reach max attempts or we create and place a room in a valid location while (tryCounterCreate != 0) { var roomSize = RNG.NextInt(RoomMinSize, RoomMaxSize + 1); var width = (int)(roomSize * RoomSizeRatioX); // This helps with non square fonts. So rooms don't look odd var height = (int)(roomSize * RoomSizeRatioY); // When accounting for font ratios, these adjustments help prevent all rooms // having the same looking square format var adjustmentBase = roomSize / 4; if (adjustmentBase != 0) { var adjustment = RNG.NextInt(-adjustmentBase, adjustmentBase + 1); var adjustmentChance = RNG.NextInt(0, 2); if (adjustmentChance == 0) { width += (int)(adjustment * RoomSizeRatioX); } else if (adjustmentChance == 1) { height += (int)(adjustment * RoomSizeRatioY); } } width = Math.Max(RoomMinSize, width); height = Math.Max(RoomMinSize, height); // Keep room interior odd, helps with placement + tunnels around the outside. if (width % 2 == 0) { width += 1; } if (height % 2 == 0) { height += 1; } var roomInnerRect = new Rectangle(0, 0, width, height); var tryCounterPlace = MaxPlacementAttempts; // Try to place the room we've created until either it doesn't intersect any other rooms, or we reach max retries (in which case, we will scrap the room entirely, create a new one, and try again) while (tryCounterPlace != 0) { int xPos = 0, yPos = 0; // Generate the rooms at odd positions, to make door/tunnel placement easier while (xPos % 2 == 0) { xPos = RNG.NextInt(3, wallFloorContext.Width - roomInnerRect.Width - 3); } while (yPos % 2 == 0) { yPos = RNG.NextInt(3, wallFloorContext.Height - roomInnerRect.Height - 3); } // Record a rectangle for the inner and outer bounds of the room we've created roomInnerRect = roomInnerRect.WithPosition(new Point(xPos, yPos)); var roomBounds = roomInnerRect.Expand(3, 3); // Check if the room intersects with any floor tile on the map already. We do it this way instead of checking against only the rooms list // to ensure that if some other map generation step placed things before we did, we don't intersect those. var intersected = false; foreach (var point in roomBounds.Positions()) { if (wallFloorContext[point]) { intersected = true; break; } } // If we intersected floor tiles, try to place the room again if (intersected) { tryCounterPlace--; continue; } // Once we place it in a valid location, update the wall/floor context, and add the room to the list of rooms. foreach (var point in roomInnerRect.Positions()) { wallFloorContext[point] = true; } placed = true; roomsContext.Add(roomInnerRect, Name); break; } if (placed) { yield return(null); break; } tryCounterCreate--; } roomCounter--; } }