Skip to content

Commit

Permalink
Merge pull request #323 from Chris3606/fix-usings-docs
Browse files Browse the repository at this point in the history
Added Using Statements to Docs
  • Loading branch information
Chris3606 authored Sep 4, 2023
2 parents 114a411 + 7e6e8fc commit 559c07b
Show file tree
Hide file tree
Showing 11 changed files with 436 additions and 359 deletions.
3 changes: 3 additions & 0 deletions GoRogue.Docs/articles/howtos/dice-rolling.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ title: Dice Rolling
# Dice Notation Parser
GoRogue contains a versatile [dice notation](https://en.wikipedia.org/wiki/Dice_notation) parser that allows you to roll virtual dice. The parser allows you to enter complex dice notation expressions, and emulate the result of rolling them via GoRogue's random number generation framework.

The code examples below will assume that the following "using" statements are in scope:
[!code-csharp[](../../../GoRogue.Snippets/HowTos/DiceNotation.cs#Usings)]

# Rolling Dice Expressions
The simplest way to use the dice roller is to use the provided `Roll` method:

Expand Down
3 changes: 3 additions & 0 deletions GoRogue.Docs/articles/howtos/fov.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ GoRogue offers algorithms for calculating [field of view](https://www.roguebasin

FOV algorithms, like most algorithms in GoRogue, depend on [grid views](~/articles/howtos/grid-view-concepts.md) to take input and produce output. Grid views are an abstraction over a 2D grid which is explained in detail at the article linked above; it is recommended that you review this article first if you are unfamiliar with grid views. The code examples here will gloss over the details of grid views, and will instead focus on FOV itself.

Also note that the code examples in this article will assume the following "using" statements are within scope:
[!code-csharp[](../../../GoRogue.Snippets/HowTos/FOV.cs#Usings)]

>[!NOTE]
> If you are using classes from the `GoRogue.GameFramework` namespace, please note that `Map` has a built-in property called `PlayerFOV`. This field uses the FOV abstraction discussed here, and will by default be set to an FOV implementation which gets its grid data based on the objects on the map. When using `GoRogue.GameFramework`, this article will be helpful in terms of teaching you how FOV works, but note that you do not have to create the instance yourself unless you want to use a different algorithm.
Expand Down
28 changes: 16 additions & 12 deletions GoRogue.Docs/articles/howtos/map-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ title: Map Generation
---

# Map Generation
The map generation framework has undergone a complete redesign in GoRogue 3.0. The new framework is designed to drastically increase the flexibility of the system by making it easier to design, debug, and use modular algorithms that perform various elements of map generation. It aims to maintain an API that is easy to get started with, but also to provide users with the ability to easily use the built-in algorithms in a custom order, and even allow users to create their own map generation methods, all within the provided sytem.
The map generation framework has undergone a complete redesign in GoRogue 3.0. The new framework is designed to drastically increase the flexibility of the system by making it easier to design, debug, and use modular algorithms that perform various elements of map generation. It aims to maintain an API that is easy to get started with, but also to provide users with the ability to easily use the built-in algorithms in a custom order, and even allow users to create their own map generation methods, all within the provided system.

Note that the code examples in this article will assume the following "using" statements are within scope:

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#Usings)]

# Getting Started
Getting started with GoRogue map generation requires only a few lines of code. GoRogue provides built-in generation algorithms to generate various types of maps in its [DefaultAlgorithms](xref:GoRogue.MapGeneration.DefaultAlgorithms) static class. Each pre-built algorithm is a function you can call that retrieves a list of [GenerationSteps](xref:GoRogue.MapGeneration.GenerationStep) that you simply pass to a generator and use to generate your map.

## Generating the Map
For example, to generate a basic roguelike dungeon with rectangular rooms connected by a maze, you would write the following code:

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/Basic.cs#GeneratingMap)]
[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#GeneratingMap)]

The functions in `DefaultAlgorithms`, including `DungeonMazeMapSteps()`, have a number of optional arguments that allow you to control the parameters of the map generated. The API documentation for those functions will explain exactly how the algorithms generate their maps, and what each parameter does. You can substitute the call to `DungeonMazeMapSteps` above for any of the functions in `DefaultAlgorithms` to generate that type of map instead.

Expand All @@ -20,7 +24,7 @@ After the map has been generated, the data that describes the map is stored with

So, after `generator.ConfigAndGenerateSafe()` above, we can access the simplest data of the map like this:

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/Basic.cs#AccessingMap)]
[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#AccessingMap)]

Other data about the map, such as the locations of doors, rectangles representing rooms generated, and more can be found in other components on the context. The components present will depend on the map generation algorithm, so refer to the `DefaultAlgorithms` function documentation, which will list the components that will be placed on the context and the data they store.

Expand All @@ -34,10 +38,10 @@ GoRogue's map generation system, in essence, provides 3 things:
3. **Built-in, complete map generation algorithms**. These are contained as functions within the `DefaultAlgorithms` class, and are nothing more than pre-built sets of built-in map generation steps. These are designed to cover basic use cases, as well as provide a quick and easy way of getting started that does not require a full understanding of the framework.

# The Framework
Creating a framework designed to accomodate arbitrary map generation algorithms is challenging. There are incredibly few assumptions about the types of data an algorithm will work with that apply to all, or even a significant subset of all, map generation algorithms. In addition, if the approach becomes too convoluted or difficult to comprehend, then it isn't useful. Therefore, the aim of the framework is to allow a flexible approach to map data, while avoiding typical traps of data flexibility like massive collections of fields, or the need to up-cast and/or assume data is present when it may not be.
Creating a framework designed to accommodate arbitrary map generation algorithms is challenging. There are incredibly few assumptions about the types of data an algorithm will work with that apply to all, or even a significant subset of all, map generation algorithms. In addition, if the approach becomes too convoluted or difficult to comprehend, then it isn't useful. Therefore, the aim of the framework is to allow a flexible approach to map data, while avoiding typical traps of data flexibility like massive collections of fields, or the need to up-cast and/or assume data is present when it may not be.

## Overview
GoRogue's map generation framework takes a component-based approach to map data. GoRogue already posesses a flexible, type-safe, and efficient system for dealing with components on objects via its [ComponentCollection](xref:GoRogue.Components.ComponentCollection). This system allows components to be added to an object, optionally associated with a "tag" string. Additionally, it allows retrieval of these components by type (eg, retrieve the component(s) on this object that are of type `T`), as well as optionally by associated tag (eg. retrieve the component on this object that is of type `T` and has the given tag associated with it). This existing system is leveraged as the core of the map generation framework.
GoRogue's map generation framework takes a component-based approach to map data. GoRogue already possesses a flexible, type-safe, and efficient system for dealing with components on objects via its [ComponentCollection](xref:GoRogue.Components.ComponentCollection). This system allows components to be added to an object, optionally associated with a "tag" string. Additionally, it allows retrieval of these components by type (eg, retrieve the component(s) on this object that are of type `T`), as well as optionally by associated tag (eg. retrieve the component on this object that is of type `T` and has the given tag associated with it). This existing system is leveraged as the core of the map generation framework.

The framework consists first of a [GenerationContext](xref:GoRogue.MapGeneration.GenerationContext), which is effectively just an object that will have one or more components representing map data attached to it.

Expand All @@ -53,7 +57,7 @@ GoRogue provides each element of the default map generation algorithms as an ind

Most of the functions of a `Generator`, including `AddStep`, return the `this` instance, so you can chain calls of them together. This makes it syntactically convenient to add many steps to a generator. The following example creates a simple map generation algorithm that simply generates rooms, then draws tunnels between rooms closest to each other to connect them.

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/Basic.cs#UsingGenerationSteps)]
[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#UsingGenerationSteps)]

## Tags for Components
The steps that are built-in to GoRogue can all operate on components with a specific tag. They'll have reasonable defaults, but allow you to customize them via constructor parameter (as we did with `RectanglesToAreas` in the above example). In most cases, `null` can be specified as the tag to indicate that any object of the correct type should qualify (although doing this is not recommended as it can make issues difficult to debug).
Expand Down Expand Up @@ -91,18 +95,18 @@ Creating completely custom map generation algorithms involves simply implementin
## Requiring Components to be Present on the Generation Context
When creating custom generation steps, it is common to require certain data as input. For example, a generation step that connects areas of a map to each other might need to take as input a list of areas to connect and/or a grid view representing tiles as wall/floor. In GoRogue's map generation framework, this requirement is represented by requiring the generation context to have a component that contains the data a step needs when it is called upon to perform its work.

This approach could lead to a lot of repetitive code in generation steps to the effect of `if(!context.Contains(typeof(Type1), typeof(Type2)) throw Exception("message")` in generation steps, and as well can lead to not-helpful exceptions (`NullReferenceExcpeption`, potentially) if the checks are forgotten. To alleviate this, `GenerationStep` implements functionality that allows you to express this pattern of requiring components easily. You simply pass the types (and, optionally, tags) of the components that are required to the constructor, and it will check for them automatically when `GenerationStep.Perform` is called. If a component is missing, a detailed exception showing which component is missing is raised. Since all of this happens before the virtual method `GenerationStep.OnPerform` (where you implement the step) is called, your code can safely assume that the components it wants are present.
This approach could lead to a lot of repetitive code in generation steps to the effect of `if(!context.Contains(typeof(Type1), typeof(Type2)) throw Exception("message")` in generation steps, and as well can lead to not-helpful exceptions (`NullReferenceException`, potentially) if the checks are forgotten. To alleviate this, `GenerationStep` implements functionality that allows you to express this pattern of requiring components easily. You simply pass the types (and, optionally, tags) of the components that are required to the constructor, and it will check for them automatically when `GenerationStep.Perform` is called. If a component is missing, a detailed exception showing which component is missing is raised. Since all of this happens before the virtual method `GenerationStep.OnPerform` (where you implement the step) is called, your code can safely assume that the components it wants are present.

The following is an example generation step that utilizes this functionality:

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/CustomGenerationSteps.cs#GenerationStepRequiringComponents)]
[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#GenerationStepRequiringComponents)]

## Creating New Components Only if Not Present
Some generation steps might want to use an existing component on the context, if an appropriate one is present, but if it isn't there, then add a new one. For example, an algorithm that creates and places rooms on a map might require a map view it can set walkable tiles to, and a list of rooms to add the rooms it generates to. However, instead of outright requiring those components to be present, since it doesn't actually _need_ any of the data for them for inputs (it's just for storing results), it's more convenient if the algorithm simply creates and adds those components if they're not already there. Since this again leads to repetitive code, the helper method `GenerationContext.GetFirstOrNew<T>` exists for this. It is exactly like `GenerationContext.GetFirst<T>`, except it also takes a function that returns a new object of the proper type, and if it can't find the component it calls the function, and adds the component it returns to the context, then returns the new component.

The following example uses this functionality to "require" two components:

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/CustomGenerationSteps.cs#GenerationStepUseOrAddComponent)]
[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#GenerationStepUseOrAddComponent)]

## Invalidating a Map
If a generation step encounters an invalid state that is not due to configuration error but rather due to rare corner cases ("bad rng", etc), ideally the map generation step would simply fix the issue, or otherwise account for that state. In some cases, however, that is impossible, and one alternative (provided the cause of the issue is known to be RNG-related and it does not occur with a high probability), is to simply throw away the map and regenerate it. GoRogue's map generation system does have built-in functionality to enable this.
Expand All @@ -115,18 +119,18 @@ Generation steps may choose to throw a `RegenerateMapException` from their `OnPe
Those functions also accept a `maxAttempts` parameter that indicates a maximum number of attempts at generating the map before it throws an exception back to the caller. This defaults to infinity, however, since default generation steps should obviously not cause infinite loops or issues here.

## Breaking OnPerform into Stages
The `GenerationStep.OnPerform` method, where you implement the logic for you generation step, allows you to break the step up into one or more "stages", which are points at which generation is pausable so you can review information in the context. This is implemented via the return type of this method, which is `IEnumerator<object?>`. This allows you to utilize the [yield functionality in C#](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield) to indicate points where the algorithm can "pause", which can be extremely useful for debugging or creating animations composed of map generation steps.
The `GenerationStep.OnPerform` method, where you implement the logic for you generation step, allows you to break the step up into one or more "stages", which are points at which generation is pauseable so you can review information in the context. This is implemented via the return type of this method, which is `IEnumerator<object?>`. This allows you to utilize the [yield functionality in C#](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/yield) to indicate points where the algorithm can "pause", which can be extremely useful for debugging or creating animations composed of map generation steps.

For example, if you had an algorithm that places rectangular rooms in a map, you might want to use a `yield return` statement after each room is placed. A generation step with an `OnPerform` implementation that does this might look something like this:

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/CustomGenerationSteps.cs#GenerationStepPauseViaEnumerable)]
[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#GenerationStepPauseViaEnumerable)]

Note that the typical `Generator.ConfigAndGenerateSafe()` method used to execute steps of map generation is written to execute all stages for all generation steps automatically, so the stages will not be evident. If you need to execute your map generation in stages, there is additional support provided (see next section).

### Completing Generation Algorithms Stage-by-Stage
To execute a set of generation steps on a `Generator` stage-by-stage, the `Generator.ConfigAndGetStageEnumeratorSafe()` is provided. It returns an `IEnumerator` that you can iterate through to complete the steps one at a time:

[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/Basic.cs#GenerateMapStageByStage)]
[!code-csharp[](../../../GoRogue.Snippets/HowTos/MapGeneration/MapGeneration.cs#GenerateMapStageByStage)]

There also exists a `Generator.GetStageEnumerator` function that does the same thing, except assumes steps are already added to the generator and does not handle `RegenerateMapException` (similar to `Generator.Generate`).

Expand Down
6 changes: 4 additions & 2 deletions GoRogue.Snippets/HowTos/DiceNotation.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using GoRogue.DiceNotation;
// ReSharper disable UnusedVariable
// ReSharper disable UnusedVariable
#region Usings
using GoRogue.DiceNotation;
#endregion

namespace GoRogue.Snippets.HowTos;
public static class DiceNotation
Expand Down
23 changes: 20 additions & 3 deletions GoRogue.Snippets/HowTos/EffectsSystem.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using GoRogue.DiceNotation;
using GoRogue.Effects;
// ReSharper disable RedundantNameQualifier

namespace GoRogue.Snippets.HowTos
{
#region EffectsBasicExample
using GoRogue.DiceNotation;
using GoRogue.Effects;

public static class EffectsBasicExample
{
class Monster
Expand Down Expand Up @@ -65,9 +67,16 @@ public static void ExampleCode()
myDamage.Trigger();
}
}

#endregion
}

namespace GoRogue.Snippets.HowTos
{
#region EffectTriggersAndDurationsExample
using GoRogue.DiceNotation;
using GoRogue.Effects;

public static class EffectTriggersAndDurationsExample
{
class Monster
Expand Down Expand Up @@ -139,9 +148,16 @@ public static void ExampleCode()

}
}

#endregion
}

namespace GoRogue.Snippets.HowTos
{
#region AdvancedEffectsExample
using GoRogue.DiceNotation;
using GoRogue.Effects;

public static class AdvancedEffectsExample
{
// This will be the class we use to pass information to our effect's Trigger function.
Expand Down Expand Up @@ -236,7 +252,8 @@ protected override void OnTrigger(out bool cancelTrigger, DamageArgs args)

int originalDamage = args.DamageTaken;
args.DamageTaken = (int)(args.DamageTaken * (1f - ReductionPct));
Console.WriteLine($"Damage taken reduced from {originalDamage} to {args.DamageTaken} by armor.");
Console.WriteLine(
$"Damage taken reduced from {originalDamage} to {args.DamageTaken} by armor.");
}
}

Expand Down
4 changes: 3 additions & 1 deletion GoRogue.Snippets/HowTos/FOV.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using GoRogue.FOV;
#region Usings
using GoRogue.FOV;
using GoRogue.MapGeneration;
using SadRogue.Primitives;
using SadRogue.Primitives.GridViews;
#endregion

namespace GoRogue.Snippets.HowTos
{
Expand Down
Loading

0 comments on commit 559c07b

Please sign in to comment.