Tag Archives: Console

RogueSharp V3 Tutorial – Monster Stats

Next Tutorial Post – Simple Combat
Previous Tutorial Post – Monster Generation

Goal

During this tutorial we’ll show the monster health bars in the right hand stats panel, but only when they are in line-of-sight to the player.

MonsterStats

Monster Stats

Adding a DrawStats() Method to Monster

As you can see in the image above, the goal for drawing monster stats is to first display the symbol of the monster in the appropriate color followed by the monster’s name. Behind the name is a health bar that will decrease based on the percentage of monster health remaining.

Open Monster.cs and add the DrawStats() method as follows:

public class Monster : Actor
{
  public void DrawStats( RLConsole statConsole, int position )
  {
    // Start at Y=13 which is below the player stats.
    // Multiply the position by 2 to leave a space between each stat
    int yPosition = 13 + ( position * 2 );

    // Begin the line by printing the symbol of the monster in the appropriate color
    statConsole.Print( 1, yPosition, Symbol.ToString(), Color );

    // Figure out the width of the health bar by dividing current health by max health
    int width = Convert.ToInt32( ( (double) Health / (double) MaxHealth ) * 16.0 );
    int remainingWidth = 16 - width;

    // Set the background colors of the health bar to show how damaged the monster is
    statConsole.SetBackColor( 3, yPosition, width, 1, Swatch.Primary );
    statConsole.SetBackColor( 3 + width, yPosition, remainingWidth, 1, Swatch.PrimaryDarkest );

    // Print the monsters name over top of the health bar
    statConsole.Print( 2, yPosition, $": {Name}", Swatch.DbLight );
  }
}

I tried to add comments to the code to help make it easier to follow along.

Updating the DungeonMap Draw() Method

Next we need to do a bit of work to DungeonMap.Draw(). It needs an extra parameter statConsole so that when we are drawing monsters, we can also draw out their stats. It is also important that we keep a count of the number of monsters that we have drawn so far. Each time we draw a new health bar stat block for a monster we want it to be below the previous one we drew so that’s why we pass this index into monster.DrawStats().

public void Draw( RLConsole mapConsole, RLConsole statConsole )
{
  // Old code
  foreach ( Cell cell in GetAllCells() )
  {
    SetConsoleSymbolForCell( mapConsole, cell );
  }

  // New code starts here ...

  // Keep an index so we know which position to draw monster stats at
  int i = 0;

  // Iterate through each monster on the map and draw it after drawing the Cells
  foreach ( Monster monster in _monsters )
  {    
    monster.Draw( mapConsole, this );
    // When the monster is in the field-of-view also draw their stats
    if ( IsInFov( monster.X, monster.Y ) )
    {      
      // Pass in the index to DrawStats and increment it afterwards
      monster.DrawStats( statConsole, i );
      i++;
    }
  }
}

Cleaning up Clear() Methods

If you remember from our previous code for our Draw() methods on DungeonMap and MessageLog we call the Clear() method on the corresponding RLConsole every time anything changes. So what this means is that if a player or monster changes position, we Clear() the whole map and redraw everything. Although this may not be the most efficient way to do things, it does make the code a lot more simple and it should be good enough for our purposes.

The problem is that currently the Clear() calls are spread out in several places. Remove the console.Clear() calls in DungeonMap.cs and MessageLog.cs

Then open Game.cs and modify the OnRootConsoleRender() method.

private static void OnRootConsoleRender( object sender, UpdateEventArgs e )
{
   if ( _renderRequired )
   {
      _mapConsole.Clear();
      _statConsole.Clear();
      _messageConsole.Clear();

      DungeonMap.Draw( _mapConsole, _statConsole );

      // Additional old code omitted...
   }
}

If you run the game now you should see the monster health bars as you find them around the map. We still can’t interact with the monsters but we will be able to soon.

As always the code for the tutorial series so far can be found on Bitbucket:
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/11MonsterStats

Bored waiting for the next tutorial post? The completed project is already available on Bitbucket.

RogueSharp V3 Tutorial – Player Stats

Next Tutorial Post – Monster Generation
Previous Tutorial Post – Message Log

Goal

During this tutorial we’ll add all of the necessary stats that the player and other actors will require to perform combat actions. We will also be rendering the player stats to the console that was previously just a placeholder on the right of the map.

DrawStats

Draw Player Stats

Combat Overview

The combat system that I’ve decided upon for the tutorial is going to be a percentage based opposing roll system. First the attacker will roll a number of 100 sided dice equal to their attack value. Each die in the roll will have a percentage chance to be a success. Each success has the potential to inflict 1 point of damage. Thus the maximum amount of damage an actor can do is their Attack value, if they get all successes and their is no defense.

So how about defense? It will work the same way. The defender rolls a number of 100 sided dice equal to their defense value. Each die has a percentage chance to be a success. The number of defender successes is subtracted from the number of attacker successes and if the remaining number is positive then that much damage is applied to the defender.

Whew! Hopefully that’s not too confusing. We’ll go through the stats one-by-one in the next section.

Defining the Stats

  • Attack – int – Number of dice to roll when performing an attack. Also represents the maximum amount of damage that can be inflicted in a single attack.
  • AttackChance – int – Percentage chance that each die rolled is a success. A success for a die means that 1 point of damage was inflicted.
  • Awareness – int – For the player this determines how far their field-of-view extends. For other actors or monsters this is the distance that they can see and hear. If the player gets within that distance then it is likely the monster will be alerted to their presence.
  • Defense– int – Number of dice to roll when defending against an attack. Also represents the maximum amount of damage that can blocked or dodged from an incoming attack.
  • DefenseChance – int – Percentage chance that each die rolled is a success. A success for a die means that 1 point of damage was blocked.
  • Gold – int – How much money the actor has. Most monsters will drop gold upon death.
  • Health – int – The current health total of the actor. If this value is 0 or less then the actor is killed.
  • MaxHealth – int – How much health the actor has when fully healed.
  • Name – string – Name of the actor
  • Speed – int – How fast the actor is. This determines the rate at which they perform actions. A lower number is faster. An actor with a speed of 10 will perform twice as many actions in the same time as an actor with a speed of 20.

Adding Stats to Actors

Before we add the stats directly to our Actor class we should first add them to the IActor interface. Open IActor.cs and update the code to look like the following.

public interface IActor
{
  int Attack { get; set; }
  int AttackChance { get; set; }
  int Awareness { get; set; }
  int Defense { get; set; }
  int DefenseChance { get; set; }
  int Gold { get; set; }
  int Health { get; set; }
  int MaxHealth { get; set; }
  string Name { get; set; }
  int Speed { get; set; }
}

Next open Actor.cs and make sure that each of these properties is implemented. Note that we won’t be doing auto-properties this time because later we’ll want to update the getters on the each property to account for additional stats from items that we equip. For instance the Defense stat might have a base value of 2, but then have an additional +2 bonus from armor that is being worn. So although it is more typing, we’ll implement these properties with their own private backing fields to allow us to add that additional functionality in later.

public class Actor : IActor, IDrawable
{
  // IActor
  private int _attack;
  private int _attackChance;
  private int _awareness;
  private int _defense;
  private int _defenseChance;
  private int _gold;
  private int _health;
  private int _maxHealth;
  private string _name;
  private int _speed;

  public int Attack
  {
    get
    {
      return _attack;
    }
    set
    {
      _attack = value;
    }
  }

  public int AttackChance
  {
    get
    {
      return _attackChance;
    }
    set
    {
      _attackChance = value;
    }
  }

  public int Awareness
  {
    get
    {
      return _awareness;
    }
    set
    {
      _awareness = value;
    }
  }

  public int Defense
  {
    get
    {
      return _defense;
    }
    set
    {
      _defense = value;
    }
  }

  public int DefenseChance
  {
    get
    {
      return _defenseChance;
    }
    set
    {
      _defenseChance = value;
    }
  }

  public int Gold
  {
    get
    {
      return _gold;
    }
    set
    {
      _gold = value;
    }
  }

  public int Health
  {
    get
    {
      return _health;
    }
    set
    {
      _health = value;
    }
  }

  public int MaxHealth
  {
    get
    {
      return _maxHealth;
    }
    set
    {
      _maxHealth = value;
    }
  }

  public string Name
  {
    get
    {
      return _name;
    }
    set
    {
      _name = value;
    } 
  }

  public int Speed
  {
    get
    {
      return _speed;
    }
    set
    {
      _speed = value;
    }
  }
}

Drawing Player Stats

Now that we have the stats out of the way, we want to be able to draw the Player stats in the upper right corner of the statConsole. Open Player.cs and set all of the starting stats for the Player in the constructor. Also add the DrawStats() method to look like the following:

public class Player : Actor
{
  public Player()
  {
    Attack = 2;
    AttackChance = 50;
    Awareness = 15;
    Color = Colors.Player;
    Defense = 2;
    DefenseChance = 40;
    Gold = 0;
    Health = 100;
    MaxHealth = 100;
    Name = "Rogue";
    Speed = 10;
    Symbol = '@';
  }

  public void DrawStats( RLConsole statConsole )
  {
    statConsole.Print( 1, 1, $"Name:    {Name}", Colors.Text );
    statConsole.Print( 1, 3, $"Health:  {Health}/{MaxHealth}", Colors.Text );
    statConsole.Print( 1, 5, $"Attack:  {Attack} ({AttackChance}%)", Colors.Text );
    statConsole.Print( 1, 7, $"Defense: {Defense} ({DefenseChance}%)", Colors.Text );
    statConsole.Print( 1, 9, $"Gold:    {Gold}", Colors.Gold );
  }
}

Make sure to also add the colors to Colors.cs. Pick any colors you like. I used these:

public static RLColor TextHeading = RLColor.White;
public static RLColor Text = Swatch.DbLight;
public static RLColor Gold = Swatch.DbSun;

Updating the Game

The final thing left to do is to update Game.cs to make sure that Player.DrawStats() is called. During the last tutorial we also added some code to test out our the message log by printing a line every time the player took a step. I’ll be deleting that code now too.

// DELETE all of the following lines
private static int _steps = 0;

MessageLog.Add( $"Step # {++_steps}" );  

_statConsole.SetBackColor( 0, 0, _statWidth, _statHeight, Swatch.DbOldStone );
_statConsole.Print( 1, 1, "Stats", Colors.TextHeading );

Then update OnRootConsoleRender() to call Player.DrawStats()

if ( _renderRequired )
{
  // Old code to keep
  DungeonMap.Draw( _mapConsole );
  Player.Draw( _mapConsole, DungeonMap );

  // New code after Player.Draw()
  Player.DrawStats( _statConsole ); 
}

If you run the program now you should get something similar to the screenshot at the top of this post.

As always the code for the tutorial series so far can be found on Bitbucket:
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/09PlayerStats

Bored waiting for the next tutorial post? The completed project is already available on Bitbucket.

RogueSharp V3 Tutorial – Message Log

Next Tutorial Post – Player Stats
Previous Tutorial Post – Connecting Rooms with Hallways

Goal

During this tutorial we’ll create a new MessageLog class that will allow us to add messages and draw them to a console. We’ll then add a bunch of messages to the log to prove that it works

MessageLog

Message Log

Designing the Message Log Class

How do we want to handle messages? We want a class that allows us to add messages to some sort of list. We also want to display some number of previous messages and have new messages scroll into view when they are added. Old messages after a certain limit should scroll out of view.

A decent structure for handling this is a Queue. You can read more about them from the link but basically you can think of it as a line at the grocery store. The first person to get in line is the first person to check out. In terms of our message log the oldest message will be the first one removed from the history.

Creating the Message Log Class

Create a new class in the Systems folder called MessageLog.cs. Add the following code to it:

// Represents a queue of messages that can be added to
// Has a method for and drawing to an RLConsole
public class MessageLog
{
  // Define the maximum number of lines to store
  private static readonly int _maxLines = 9;

  // Use a Queue to keep track of the lines of text
  // The first line added to the log will also be the first removed
  private readonly Queue<string> _lines;

  public MessageLog()
  {
    _lines = new Queue<string>();
  }

  // Add a line to the MessageLog queue
  public void Add( string message )
  {
    _lines.Enqueue( message );
    
    // When exceeding the maximum number of lines remove the oldest one.
    if ( _lines.Count > _maxLines )
    {
      _lines.Dequeue();
    }
  }

  // Draw each line of the MessageLog queue to the console
  public void Draw( RLConsole console )
  {
    console.Clear();
    string[] lines = _lines.ToArray();
    for ( int i = 0; i < lines.Length; i++ )
    {
      console.Print( 1, i + 1, lines[i], RLColor.White );
    }
  }
}

Hooking up the Message Log

All of the remaining work will be handled in Game.cs.

First in the section at the top of the file where we define Player, DungeonMap and CommandSystem add the following:

public static MessageLog MessageLog { get; private set; }

Next in the Main() method of Game.cs add some code to instantiate a new MessageLog and add a couple of messages to it. Don’t forget to also remove the old code that was setting the _messageConsole to a blue color and printing “Messages” on it.

// Create a new MessageLog and print the random seed used to generate the level
MessageLog = new MessageLog();
MessageLog.Add( "The rogue arrives on level 1" );
MessageLog.Add( $"Level created with seed '{seed}'" );

// Remove these lines:
_messageConsole.SetBackColor( 0, 0, _messageWidth, _messageHeight, Swatch.DbDeepWater );
_messageConsole.Print( 1, 1, "Messages", Colors.TextHeading );

The last thing we need to do is call MessageLog.Draw() in our OnRootConsoleRender() method near where we call DungeonMap.Draw() and Player.Draw().

MessageLog.Draw( _messageConsole );

Temporary Code for Generating Lots of Messages

Just to prove that our messages work the way we expect and remove old messages once we reach the limit we set of “9” lets log a bunch of messages. We’ll do this by adding a private member variable _steps and increment it each time the player acts.

// Temporary member variable just to show our MessageLog is working
private static int _steps = 0;

// In OnRootConsoleUpdate() replace the if ( didPlayerAct ) block
if ( didPlayerAct )
{
  // Every time the player acts increment the steps and log it
  MessageLog.Add( $"Step # {++_steps}" );  
  _renderRequired = true;
}

The code for the tutorial series so far can be found on Bitbucket:
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/08MessageLog

Closing Thoughts

I’ve been spending a bunch of time messing with RexPaint. It’s a fantastic ASCII art editor and can be used to design maps. I highly recommend you take a look at it.

Bored waiting for the next tutorial post? The completed project is already available on Bitbucket.

RogueSharp V3 Tutorial – Player

Next Tutorial Post – Player Input
Previous Tutorial Post – Simple Map Drawing

Goal

During this tutorial we’ll create interfaces for Actors and for Drawing things. We’ll then create a simple Player class that implements both of our new interfaces. We’ll draw the Player and his field of view to the screen but we will not get into accepting keyboard input and moving the Player until next time.

Creating the Interfaces

There are two new interfaces that we want to create. In C# interfaces are used to define a set of functionality or a group of related behaviors. You can think of them as a contract that classes must follow. By implementing the interface a Class is agreeing to the contract and must implement those methods and behaviors. We will always prefix our interfaces with the letter ‘I’ as is common in C#.

IActor Interface

The first interface we will create will be IActor. For now it will only have two properties, Name and Awareness. The Awareness property will be used when calculating field-of-view to determine the maximum distance the Actor is aware of, can see or sense.

Create a new folder called Interfaces and then create a new interface in the folder called IActor.cs. Place the following code inside the new file.

namespace RogueSharpV3Tutorial.Interfaces
{
  public interface IActor
  {
    string Name { get; set; }
    int Awareness { get; set; }
  }
}

IDrawable Interface

The next interface we will define will be IDrawable. The properties that we need to draw a cell in the console are a Color, Symbol, and the X and Y coordinates of the cell. We also want to have a Draw method that will take the RLConsole to draw to as well an IMap. Notice that we don’t require a concrete Map class but we will accept any class that follows the IMap contract. The IMap is important because that will allow us to calculate field-of-view.

namespace RogueSharpV3Tutorial.Interfaces
{
  public interface IDrawable
  {
    RLColor Color { get; set; }
    char Symbol { get; set; }
    int X { get; set; }
    int Y { get; set; }

    void Draw( RLConsole console, IMap map );
  }
}

Actor Base Class

Now that we have defined our interfaces we’ll make a base class called Actor that implements both of our interfaces. In C# a class can only inherit from a single class. It can however implement as many interfaces as you want.

Start by creating a new class in the Core folder called Actor.cs. It should implement both IActor and IDrawable. Place the following code in Actor.cs:

public class Actor : IActor, IDrawable
{
  // IActor
  public string Name { get; set; }
  public int Awareness { get; set; }

  // IDrawable
  public RLColor Color { get; set; }
  public char Symbol { get; set; }
  public int X { get; set; }
  public int Y { get; set; }
  public void Draw( RLConsole console, IMap map )
  {
    // Don't draw actors in cells that haven't been explored
    if ( !map.GetCell( X, Y ).IsExplored )
    {
      return;
    }

    // Only draw the actor with the color and symbol when they are in field-of-view
    if ( map.IsInFov( X, Y ) )
    {
      console.Set( X, Y, Color, Colors.FloorBackgroundFov, Symbol );
    }
    else
    {
      // When not in field-of-view just draw a normal floor
      console.Set( X, Y, Colors.Floor, Colors.FloorBackground, '.' );
    }
  }
}

Implementing the properties should be straightforward. They just have public getters and setters. The most interesting part will be the Draw() method because it needs to inspect the IMap for explored cells and cells in field-of-view so that it can draw the appropriate symbols.

Player Color

We need to establish a Color for our Player. Open Colors.cs and add the following line:

public static RLColor Player = Swatch.DbLight;

Player Class

It’s time to create the Player class. In the Core folder make a new class called Player.cs. We want it to have a base class of Actor so that it will automatically get the Draw() method and all the necessary properties. The code should look like this.

namespace RogueSharpV3Tutorial.Core
{
  public class Player : Actor
  {
    public Player()
    {
      Awareness = 15;
      Name = "Rogue";
      Color = Colors.Player;
      Symbol = '@';
      X = 10;
      Y = 10;
    }
  }
}

You’ll see that all we have to do to make the Player unique from other Actors is to define unique values for our properties. In this case the Player can see 15 cells so the Awareness is 15. We’re also setting the starting position to 10, 10 and the Color to the one we defined earlier. The Symbol is the standard ‘@’ character from the original Rogue. Feel free to adjust any of these values to anything you want.

If you’ve been following along with the tutorial your project structure should now look like this:

InterfacesFolder

Project Layout

Player Field-of-View Method

Open DungeonMap.cs and create a new method called UpdatePlayerFieldOfView() which will be called when we position the Player

// This method will be called any time we move the player to update field-of-view
public void UpdatePlayerFieldOfView()
{
  Player player = Game.Player;
  // Compute the field-of-view based on the player's location and awareness
  ComputeFov( player.X, player.Y, player.Awareness, true );
  // Mark all cells in field-of-view as having been explored
  foreach ( Cell cell in GetAllCells() )
  {
    if ( IsInFov( cell.X, cell.Y ) )
    {
      SetCellProperties( cell.X, cell.Y, cell.IsTransparent, cell.IsWalkable, true );
    }
  }
}

Adding the Player to the Game

The last thing that we have to do in this tutorial is add the Player to the Game. Open Game.cs and add a Player property just above our existing DungeonMap property.

public static Player Player { get; private set; }

Next we need to construct a new Player in our Main() method. Don’t forget to update the field-of-view by calling DungeonMap.UpdatePlayerFieldOfView().

Player = new Player();
// The next two lines already existed
MapGenerator mapGenerator = new MapGenerator( _mapWidth, _mapHeight );
DungeonMap = mapGenerator.CreateMap();
// End of existing code
DungeonMap.UpdatePlayerFieldOfView();

We’re almost done. The last thing we need to do is call Player.Draw() in our OnRootConsoleRender() method.

Player.Draw( _mapConsole, DungeonMap );

If all went well when you start your game you should see this:

MapWithPlayer

Map with Player

The code after all of the changes so far can be found here:
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/04PlayerActor

Closing Thoughts

For the near future expect 1-2 posts per week. This is a large series and I expect there to be 30 or more posts to complete the tutorial. I appreciate all the comments and feedback. For those of you that private messaged me in BitBucket I’m embarrassed to know that there was an inbox there until recently so I apologize for not answering your messages.

RogueSharp V3 Tutorial – Simple Map Drawing

Next Tutorial Post – Player
Previous Tutorial Post – Color Palette

Goal

In this tutorial we’ll begin using the Map features of RogueSharp. We will create our own DungeonMap class that will start off very simple but give us lots of room for growth. We’ll also be drawing this to the map sub console and displaying it on the screen.

Creating the DungeonMap

When it comes to the Map class in RogueSharp it is designed to be generic so that it can be used in a variety of games. It has plenty of functionality for path-finding, determining field-of-view, selecting Cells and more.

What RogueSharp doesn’t have is any concept of doors, stairs, traps, bookcases, torches, or any other dungeon features that are specific to an individual game’s domain. These are really up to the game designer and not something that I feel should be baked into an engine.

With that said, we do really want all of these features in our game, so how can we add them and still use RogueSharp? The answer is we can create our own class that inherits all of the features of RougeSharp’s Map class but at the same time extends it with the features that are specific to our game.

Start by creating a new class in the Core folder called DungeonMap.cs and add the following code to it.

// Our custom DungeonMap class extends the base RogueSharp Map class
public class DungeonMap : Map
{
  // The Draw method will be called each time the map is updated
  // It will render all of the symbols/colors for each cell to the map sub console
  public void Draw( RLConsole mapConsole )
  {
    mapConsole.Clear();
    foreach ( Cell cell in GetAllCells() )
    {
      SetConsoleSymbolForCell( mapConsole, cell );
    }
  }

  private void SetConsoleSymbolForCell( RLConsole console, Cell cell )
  {
    // When we haven't explored a cell yet, we don't want to draw anything
    if ( !cell.IsExplored )
    {
      return;
    }

    // When a cell is currently in the field-of-view it should be drawn with ligher colors
    if ( IsInFov( cell.X, cell.Y ) )
    {
      // Choose the symbol to draw based on if the cell is walkable or not
      // '.' for floor and '#' for walls
      if ( cell.IsWalkable )
      {
        console.Set( cell.X, cell.Y, Colors.FloorFov, Colors.FloorBackgroundFov, '.' );
      }
      else
      {
        console.Set( cell.X, cell.Y, Colors.WallFov, Colors.WallBackgroundFov, '#' );
      }
    }
    // When a cell is outside of the field of view draw it with darker colors
    else
    {
      if ( cell.IsWalkable )
      {
        console.Set( cell.X, cell.Y, Colors.Floor, Colors.FloorBackground, '.' );
      }
      else
      {
        console.Set( cell.X, cell.Y, Colors.Wall, Colors.WallBackground, '#' );
      }
    }
  }
}

You’ll notice on line 7 that our DungeonMap class inherits from the base RogueSharp Map class. This means that we already get access to everything that Map can do!

The drawing code is pretty simple. We iterate through every Cell in the Map and then choose how to display it based on if it is explored, if it is walkable, and if it is in field-of-view or not.

At this point we don’t have any special map features that actually require us to have our own class. Don’t worry though, those features are only a few tutorials away.

Creating the MapGenerator

Now we need a class that will be responsible for generating interesting maps for us. Make a new folder called Systems and inside the folder create a new class called MapGenerator.cs

To start off with we’ll make one of the simplest maps. It will be all open floors with a wall around the entire edge of the map.

Add the following code to MapGenerator.cs

public class MapGenerator
{
  private readonly int _width;
  private readonly int _height;

  private readonly DungeonMap _map;

  // Constructing a new MapGenerator requires the dimensions of the maps it will create
  public MapGenerator( int width, int height )
  {
    _width = width;
    _height = height;
    _map = new DungeonMap();
  }

  // Generate a new map that is a simple open floor with walls around the outside
  public DungeonMap CreateMap()
  {
    // Initialize every cell in the map by
    // setting walkable, transparency, and explored to true
    _map.Initialize( _width, _height );
    foreach ( Cell cell in _map.GetAllCells() )
    {
      _map.SetCellProperties( cell.X, cell.Y, true, true, true );
    }

    // Set the first and last rows in the map to not be transparent or walkable
    foreach ( Cell cell in _map.GetCellsInRows( 0, _height - 1 ) )
    {
      _map.SetCellProperties( cell.X, cell.Y, false, false, true );
    }

    // Set the first and last columns in the map to not be transparent or walkable
    foreach ( Cell cell in _map.GetCellsInColumns( 0, _width - 1 ) )
    {
      _map.SetCellProperties( cell.X, cell.Y, false, false, true );
    }

    return _map;
  }
}

As with the DungeonMap, the MapGenerator also is not that interesting at this point but it gives us a solid foundation for adding features in the future.

Your project file structure should now look something like this:

ProjectStructureWithSystems

Project Structure

Hooking up the MapGenerator and Drawing

To start using our DungeonMap we need to first add it to Game.cs

public static DungeonMap DungeonMap { get; private set; }

Then in our Main() method of Game.cs we need to use our MapGenerator to create our DungeonMap.

MapGenerator mapGenerator = new MapGenerator( _mapWidth, _mapHeight );
DungeonMap = mapGenerator.CreateMap();

Now that the map is created all that is left is to draw it in our OnRootConsoleRender() method of Game.cs

DungeonMap.Draw( _mapConsole );

If everything worked when you run the game you should now see the map console has been replaced with this:

SimpleMapDrawing

Simple Map Drawing

If it didn’t work, don’t worry. The complete code so far can be found here:
https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/03BasicMap

Also, feel free to leave me comments if you have questions. I’ll do my best to answer them quickly.

RogueSharp V3 Tutorial – Multiple Consoles

Next Tutorial Post – Color Palette
Previous Tutorial Post – Creating the Project

Goal

In this tutorial we’ll create sub consoles for each part of our game window and Blit them to the root console.

FourConsoles

Four Sub Consoles

Sub Consoles

Our game is more than a map. It will also have areas for the inventory, player statistics, and a message log. We could just draw all these directly to the root console but it will be better to draw each to their own console and Blit them to the root in their correct positions.

Defining Console Dimensions

First we need to define the width and height of each of our sub-consoles and declare each RLConsole. At the top of Game.cs add the following code:

public static class Game
{
  // The screen height and width are in number of tiles
  private static readonly int _screenWidth = 100;
  private static readonly int _screenHeight = 70;
  private static RLRootConsole _rootConsole;

  // The map console takes up most of the screen and is where the map will be drawn
  private static readonly int _mapWidth = 80;
  private static readonly int _mapHeight = 48;
  private static RLConsole _mapConsole;

  // Below the map console is the message console which displays attack rolls and other information
  private static readonly int _messageWidth = 80;
  private static readonly int _messageHeight = 11;
  private static RLConsole _messageConsole;

  // The stat console is to the right of the map and display player and monster stats
  private static readonly int _statWidth = 20;
  private static readonly int _statHeight = 70;
  private static RLConsole _statConsole;

  // Above the map is the inventory console which shows the players equipment, abilities, and items
  private static readonly int _inventoryWidth = 80;
  private static readonly int _inventoryHeight = 11;
  private static RLConsole _inventoryConsole;
  
  // ... additional code omitted
}

Instantiate the Consoles

Now we need to new up our four consoles with the widths and heights that we defined. In our Main() just after the line where we make a new RLRootConsole add the following code:

public static void Main()
{
  // ... previous code omitted
  // After the line that starts _rootConsole = new RLRootConsole( ...  

  // Initialize the sub consoles that we will Blit to the root console
  _mapConsole = new RLConsole( _mapWidth, _mapHeight );
  _messageConsole = new RLConsole( _messageWidth, _messageHeight );
  _statConsole = new RLConsole( _statWidth, _statHeight );
  _inventoryConsole = new RLConsole( _inventoryWidth, _inventoryHeight );
  
  // ... additional code omitted
}

Set Background Color and Label

Next we’ll want to set the background color of each console and put a label on them so that we can verify that they are in the correct positions when we Blit them to the Root console. In OnRootConsoleUpdate()  replace _rootConsole.Print( 10, 10, “It worked!”, RLColor.White ); with the following code:

private static void OnRootConsoleUpdate( object sender, UpdateEventArgs e )
{
  // Set background color and text for each console 
  // so that we can verify they are in the correct positions
  _mapConsole.SetBackColor( 0, 0, _mapWidth, _mapHeight, RLColor.Black );
  _mapConsole.Print( 1, 1, "Map", RLColor.White );  

  _messageConsole.SetBackColor( 0, 0, _messageWidth, _messageHeight, RLColor.Gray );
  _messageConsole.Print( 1, 1, "Messages", RLColor.White );

  _statConsole.SetBackColor( 0, 0, _statWidth, _statHeight, RLColor.Brown );
  _statConsole.Print( 1, 1, "Stats", RLColor.White );

  _inventoryConsole.SetBackColor( 0, 0, _inventoryWidth, _inventoryHeight, RLColor.Cyan );
  _inventoryConsole.Print( 1, 1, "Inventory", RLColor.White );
}

Blit and Render

The final step we’ll need to take is to update OnRootConsoleRender() to Blit each of our consoles to the root console in the correct positions. The RLNET method we will use is RLConsole.Blit which takes several parameters.

  • A source console
  • The X and Y position of the top left corner of a rectangular area from the source
  • The width and height of the rectangular area from the source that we will be Blitting
  • A destination console to Blit to
  • The X and Y position of the top left corner of where we will Blit to in the destination console
private static void OnRootConsoleRender( object sender, UpdateEventArgs e )
{
  // Blit the sub consoles to the root console in the correct locations
  RLConsole.Blit( _mapConsole, 0, 0, _mapWidth, _mapHeight, 
    _rootConsole, 0, _inventoryHeight );
  RLConsole.Blit( _statConsole, 0, 0, _statWidth, _statHeight, 
    _rootConsole, _mapWidth, 0 );
  RLConsole.Blit( _messageConsole, 0, 0, _messageWidth, _messageHeight, 
    _rootConsole, 0, _screenHeight - _messageHeight );
  RLConsole.Blit( _inventoryConsole, 0, 0, _inventoryWidth, _inventoryHeight, 
    _rootConsole, 0, 0 );

  // Tell RLNET to draw the console that we set
  _rootConsole.Draw();
}

And that’s it! If you run the program now you should see output similar to the screenshot at the beginning of this post.

Complete code for this post – https://bitbucket.org/FaronBracy/roguesharpv3tutorial/commits/tag/01SetupConsoles

Closing thoughts

So far we haven’t used RogueSharp at all. I think it is necessary to do some of these basic setup tasks to pave the way for the future. Don’t worry though we’ll be getting into RogueSharp functionality shortly.

RogueSharp V3 Tutorial – Creating the Project

Next tutorial post – Multiple Consoles
Previous tutorial post – Introduction and Goals

Goal

In this tutorial we’ll get the RogueSharp and RLNET NuGet packages added to a brand new project.

Creating a new Console Project

I’ll be using Visual Studio 2015 for this project. The community edition can be found here: https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx

  1. Go to the File menu and choose New -> Project…
  2. From Templates choose Visual C# -> Windows -> Console Application
  3. Give it any name and location that want.
Console Application

Console Application

Adding NuGet Packages

Both RogueSharp and RLNET are available via NuGet and are easy to add to our project.

  1. Right click on the project (the name of your project will be whatever you named it in step 3 above)
  2. Choose Manage NuGet Packages… from the context menu.
Manage NuGet Packages

Manage NuGet Packages

Now you should be able to choose Browse and search for RogueSharp and RLNET. Make sure that the Package Source dropdown is set to nuget.org Click Install for both packages. As of this writing the latest version of RogueSharp is 3.0.0 and RLNET is 1.0.6. Note RLNet has a dependency on OpenTK so it will also install v1.1.225 of that library.

Adding a Font File

RLNET needs a special font file like libtcod uses. If you aren’t familiar with this and want to know more about it there is good information here.

But for our purposes just right click and download this image and place it in your project directory.

Save to project directory

Save to project directory

Once the file is in your project directory, you should be able to Right Click on your project in Visual Studio and choose Add -> Existing Item… Choose the image that you saved and it should show up in the project. Make sure to set the Properties of this file to Copy always.

Set Copy Always

Set Copy Always

The Code

A console application always includes a file called Program.cs which has a single Main() function which serves as the entry point.

Rename Program.cs to Game.cs

Now we’re going to add a bit of code to render a RLRootConsole with the text “It worked!” just to prove that we hooked up the NuGet packages correctly. I won’t go into a lot of detail here but if you want more depth check out these other tutorials.

using RLNET;

namespace RogueSharpV3Tutorial
{
  public class Game
  {
    // The screen height and width are in number of tiles
    private static readonly int _screenWidth = 100;
    private static readonly int _screenHeight = 70;

    private static RLRootConsole _rootConsole;

    public static void Main()
    {
      // This must be the exact name of the bitmap font file we are using or it will error.
      string fontFileName = "terminal8x8.png";
      // The title will appear at the top of the console window
      string consoleTitle = "RougeSharp V3 Tutorial - Level 1";
      // Tell RLNet to use the bitmap font that we specified and that each tile is 8 x 8 pixels
      _rootConsole = new RLRootConsole( fontFileName, _screenWidth, _screenHeight, 
        8, 8, 1f, consoleTitle );
      // Set up a handler for RLNET's Update event
      _rootConsole.Update += OnRootConsoleUpdate;
      // Set up a handler for RLNET's Render event
      _rootConsole.Render += OnRootConsoleRender;
      // Begin RLNET's game loop
      _rootConsole.Run();
    }

    // Event handler for RLNET's Update event
    private static void OnRootConsoleUpdate( object sender, UpdateEventArgs e )
    {
      _rootConsole.Print( 10, 10, "It worked!", RLColor.White );
    }

    // Event handler for RLNET's Render event
    private static void OnRootConsoleRender( object sender, UpdateEventArgs e )
    {
      // Tell RLNET to draw the console that we set
      _rootConsole.Draw();
    }
  }
}

If you run your project now you should see something like this:

RootConsoleRender

It Worked!

Here is the link to the final code on BitBucket

Using RogueSharp with RLNET Console

People have asked me if it is possible to use RogueSharp without using MonoGame. They’ve also asked how to use RogueSharp to make a classic ASCII game. Though MonoGame is a great framework there are plenty of other ways to create games in C#. Today I’m going to show how to use RogueSharp with RLNET which is a lightweight API to help quickly create tile-based games for .NET.

Large Cavern

Large Cavern

Goal

In this tutorial we’ll get RogueSharp and RLNET added to a brand new project. We’ll render the console which will be a cave-like map and intercept keyboard commands to be able to move the player represented by an ‘@’ through the ASCII cave. We’ll calculate field-of-view and render what the player can see as well as what they have previously explored. Best of all we’ll do this in just a few lines of code.

Creating a new Console Project

I’ll be using Visual Studio for this Project.

  1. Go to the File menu and choose New -> Project…
  2. From Templates choose Visual C# -> Windows Desktop -> Console Application
  3. Give it any name and location that want.
Console Application

Console Application

Adding NuGet Packages

Both RogueSharp and RLNET are available via NuGet and are easy to add to our project.

  1. Right click on the project (the name of your project will be whatever you named it in step 3 above)
  2. Choose Manage NuGet Packages… from the context menu.
Manage NuGet Packages

Manage NuGet Packages

Now you should be able to choose Online -> nuget.org and search for RogueSharp and RLNET. Click Install for both packages. As of this writing the latest version of RogueSharp is 2.0.0.0

Adding Nuget Packages

Adding Nuget Packages

Adding a Font File

RLNET needs a special font file like libtcod uses. If you aren’t familiar with this and want to know more about it there is good information here.

But for our purposes just right click and download this image and place it in your project directory.

Save to project directory

Save to project directory

Once the file is in your project directory, you should be able to Right Click on your project in Visual Studio and choose Add -> Existing Item… Choose the image that you saved and it should show up in the project. Make sure to set the Properties of this file to Copy always.

Set Copy Always

Set Copy Always

The Code

Because I’ve already produced a bunch of tutorials for RogueSharp and RLNET has it’s own set of tutorials I’m not going to explain each step. The code is only about 100 lines with comments and extra spacing and hopefully my comments are sufficient to help you out. Just copy and paste the code block below into Program.cs

using RLNET;
using RogueSharp;

namespace RogueSharpRLNetSamples
{
  public class Program
  {
    // The screen height and width are in number of tiles
    private static readonly int _screenWidth = 50;
    private static readonly int _screenHeight = 50;
    // The starting position for the player
    private static int _playerX = 25;
    private static int _playerY = 25;
    private static RLRootConsole _rootConsole;
    private static IMap _map;

    public static void Main()
    {
      // Use RogueSharp to create a new cave map the same size as the screen.
      _map = Map.Create( new CaveMapCreationStrategy<Map>( _screenWidth, _screenHeight, 45, 4, 3 ) );
      // This must be the exact name of the bitmap font file we are using or it will error.
      string fontFileName = "terminal8x8.png";
      // The title will appear at the top of the console window
      string consoleTitle = "RougeSharp RLNet Tutorial" ;
      // Tell RLNet to use the bitmap font that we specified and that each tile is 8 x 8 pixels
      _rootConsole = new RLRootConsole( fontFileName, _screenWidth, _screenHeight, 8, 8, 1f, consoleTitle );
      // Set up a handler for RLNET's Update event
      _rootConsole.Update += OnRootConsoleUpdate;
      // Set up a handler for RLNET's Render event
      _rootConsole.Render += OnRootConsoleRender;
      // Begin RLNET's game loop
      _rootConsole.Run();
    }

    // Event handler for RLNET's Update event
    private static void OnRootConsoleUpdate( object sender, UpdateEventArgs e )
    {
      RLKeyPress keyPress = _rootConsole.Keyboard.GetKeyPress();
      if ( keyPress != null )
      {
        // Check for the Up key press
        if ( keyPress.Key == RLKey.Up )
        {
          // Check the RogueSharp map to make sure the Cell is walkable before moving
          if ( _map.GetCell( _playerX, _playerY - 1 ).IsWalkable )
          {
            // Update the player position
            _playerY--;
          }
        }
        // Repeat for the other directions
        else if ( keyPress.Key == RLKey.Down )
        {
          if ( _map.GetCell( _playerX, _playerY + 1 ).IsWalkable )
          {
            _playerY++;
          }
        }
        else if ( keyPress.Key == RLKey.Left )
        {
          if ( _map.GetCell( _playerX - 1, _playerY ).IsWalkable )
          {
            _playerX--;
          }
        }
        else if ( keyPress.Key == RLKey.Right )
        {
          if ( _map.GetCell( _playerX + 1, _playerY ).IsWalkable )
          {
            _playerX++;
          }
        }
      }
    }

    // Event handler for RLNET's Render event
    private static void OnRootConsoleRender( object sender, UpdateEventArgs e )
    {
      _rootConsole.Clear();

      // Use RogueSharp to calculate the current field-of-view for the player
      _map.ComputeFov( _playerX, _playerY, 50, true );

      foreach ( var cell in _map.GetAllCells() )
      {
        // When a Cell is in the field-of-view set it to a brighter color
        if ( cell.IsInFov )
        {
          _map.SetCellProperties( cell.X, cell.Y, cell.IsTransparent, cell.IsWalkable, true );
          if ( cell.IsWalkable )
          {
            _rootConsole.Set( cell.X, cell.Y, RLColor.Gray, null, '.' );
          }
          else
          {
            _rootConsole.Set( cell.X, cell.Y, RLColor.LightGray, null, '#' );
          }
        }
        // If the Cell is not in the field-of-view but has been explored set it darker
        else if ( cell.IsExplored )
        {
          if ( cell.IsWalkable )
          {
            _rootConsole.Set( cell.X, cell.Y, new RLColor( 30, 30, 30 ), null, '.' );
          }
          else
          {
            _rootConsole.Set( cell.X, cell.Y, RLColor.Gray, null, '#' );
          }
        }
      }

      // Set the player's symbol after the map symbol to make sure it is draw
      _rootConsole.Set( _playerX, _playerY, RLColor.LightGreen, null, '@' );

      // Tell RLNET to draw the console that we set
      _rootConsole.Draw();
    }
  }
}

If you run your project now you should see something like this:

RougeSharp and RLNET Starter Project

RougeSharp and RLNET Starter Project

Here is the link to the final code on BitBucket

Additional Fun

The eleventh Seven Day Roguelike Challenge is nearly upon us! I urge you to participate. If you don’t like RogueSharp or C# there are plenty of other RogueLike libraries out there for most languages.

There are also more .NET console libraries that should work well with RougeSharp. Why don’t you give them a try, or go further with RLNET. If you haven’t used C# before maybe try it for your 7DRL this year.

  • SadConsole – Works with MonoGame! I haven’t tried it yet but want to soon.
  • Malison – I have not tried this one yet either but it looks promising.
  • RLNET – What we used in this tutorial.