Building the Really Really Really Simple RogueLike v0.1 with C#


Really Really Really Simple Roguelike V0.1

Want to build a Roguelike with C# in 5 minutes? The following is a really quick game I built in about 2 hours and shows how easy it is to get a simple game going.

Its a very very very simple game – all you do is pickup a sword before the monster gets to you and you win. Obviously, if the monster gets to you first – well you can work out the ending.

What do you need?

C# on Visual Studio 2005/2008 or 2010. Any version should be fine as long as you can create a console application.

Now we are going to add the following into 1 file in Visual Studio rather than using multiple files and whilst this is not the best practice for a serious application,
we are doing this to keep things really simple, and to display the entire application on a few pages of paper.

Here we go

1.Start Visual Studio 2010

2.File->New Project

3.Select the Windows->Console Application

4.Name the Project ReallyReallyRealySimpleRogueLike and click OK

5.Right click on References->Add Reference…

6.Click on Assemblies then Framework and select System.Drawing

7.At the top of Program.cs insert the following line  using System.Drawing;

8.Replace the program class with the following

 class ReallyReallyReallySimpleRogueLike
    {
        static void Main(string[] args)
        {
            Dungeon dungeon = new Dungeon(Constants.DungeonWidth, Constants.DungeonHeight);
            string displayText = Constants.IntroductionText;

            while (dungeon.IsGameActive)
            {
                dungeon.DrawToConsole();
                Console.WriteLine(displayText);
                Console.Write(Constants.CursorImage);
                displayText = dungeon.ExecuteCommand(Console.ReadKey());
            }

            Console.WriteLine(ConcludeGame(dungeon));
            Console.ReadLine();
        }

        private static string ConcludeGame(Dungeon dungeon)
        {
            return (dungeon.player.Hits > 0) ? Constants.PlayerWinsText : Constants.MonsterWinsText;
        }
    }

9.Now add the following class

 class Dungeon
    {
        Random r;
        public Player player;
        List monsters;
        List swords;
        List walls;

        public Tile[,] Tiles;
        private int xMax;
        private int yMax;
        public enum Direction
        {
            North,
            South,
            East,
            West
        }

        public bool IsGameActive
        {
            get
            {
                return (player.Hits > 0 && monsters.Any(m => m.Hits > 0));
            }
        }

        public Dungeon(int xMax, int yMax)
        {
            monsters = new List();
            walls = new List();
            swords = new List();

            this.xMax = xMax;
            this.yMax = yMax;
            Tiles = new Tile[xMax, yMax];
            BuildRandomDungeon();
            SetDungeonTiles();
        }

        public string ExecuteCommand(ConsoleKeyInfo command)
        {
            string commandResult = ProcessCommand(command);
            ProcessMonsters();
            SetDungeonTiles();

            return commandResult;
        }

        private void ProcessMonsters()
        {
            if (monsters != null && monsters.Count > 0)
            {
                monsters.Where(m => m.Hits >= 0).ToList().ForEach(m =>
                {
                    MoveMonsterToPlayer(m);
                });
            }
        }

        private void BuildRandomDungeon()
        {
            r = new Random();
            SetAllDungeonSquaresToTiles();

            for (int i = 0; i < xMax; i++)
            {
                Wall top = new Wall(i, 0);
                walls.Add(top);
                Wall bottom = new Wall(i, yMax - 1);
                walls.Add(bottom);
            }

            for (int i = 0; i < yMax; i++)
            {
                Wall left = new Wall(0, i);
                walls.Add(left);
                Wall right = new Wall(xMax - 1, i);
                walls.Add(right);
            }

            for (int i = 0; i < Constants.NumberOfSwords; i++)
            {
                Sword s = new Sword(GetValidRandomPoint());
                swords.Add(s);
            }

            for (int i = 0; i  0 && monster.X  player.X) ? -1 : 1;

            if ((monster.Y > 0 && monster.Y  player.Y) ? -1 : 1;

            if (!IsInvalidValidMove(move.X, move.Y))
            {
                monster.X = move.X;
                monster.Y = move.Y;
            }

            if (monster.X == player.X && monster.Y == player.Y)
                ResolveCombat(monster);
        }

        private void ResolveCombat(Monster monster)
        {
            if (player.Inventory.Any())
                monster.Die();
            else
                player.Die();
        }

        public string ProcessCommand(ConsoleKeyInfo command)
        {

            string output = string.Empty;
            switch(command.Key)
            {
                case ConsoleKey.UpArrow:
                case ConsoleKey.DownArrow:
                case ConsoleKey.RightArrow:
                case ConsoleKey.LeftArrow:
                    output = GetNewLocation(command, new Point(player.X, player.Y));
                    break;
                case ConsoleKey.F1:
                    output = Constants.NoHelpText;
                    break;
            }

            return output;
        }

        private string GetNewLocation(ConsoleKeyInfo command, Point move)
        {
            switch (command.Key)
            {
                case ConsoleKey.UpArrow:
                    move.Y -= 1;
                    break;
                case ConsoleKey.DownArrow:
                    move.Y += 1;
                    break;
                case ConsoleKey.RightArrow:
                    move.X += 1;
                    break;
                case ConsoleKey.LeftArrow:
                    move.X -= 1;
                    break;
            }

            if (!IsInvalidValidMove(move.X, move.Y))
            {
                player.X = move.X;
                player.Y = move.Y;
                if (Tiles[move.X, move.Y] is Sword && player.Inventory.Count == 0)
                {
                    Sword sword = (Sword)Tiles[move.X, move.Y];
                    player.Inventory.Add(sword);
                    swords.Remove(sword);
                }
                return Constants.OKCommandText;
            }
            else
                return Constants.InvalidMoveText;
        }

        public bool IsInvalidValidMove(int x, int y)
        {
            return (x == 0 || x == Constants.DungeonWidth - 1 || y == Constants.DungeonHeight - 1 || y == 0);
        }

        public void SetDungeonTiles()
        {
            //Draw the empty dungeon
            SetAllDungeonSquaresToTiles();

            SetAllDungeonObjectsToTiles();
        }

        private void SetAllDungeonObjectsToTiles()
        {
            //Now draw each of the parts of the dungeon
            walls.ForEach(w => Tiles[w.X, w.Y] = w);
            swords.ForEach(s => Tiles[s.X, s.Y] = s);
            monsters.ForEach(m => Tiles[m.X, m.Y] = m);
            Tiles[player.X, player.Y] = player;
        }

        private void SetAllDungeonSquaresToTiles()
        {
            for (int i = 0; i < yMax; i++)
            {
                for (int j = 0; j < xMax; j++)
                {
                    Tiles[j, i] = new Tile(i, j);
                }
            }
        }

        public void DrawToConsole()
        {
            Console.Clear();
            for (int i = 0; i < yMax; i++)
            {
                for (int j = 0; j < xMax; j++)
                {
                    Console.ForegroundColor = Tiles[j, i].Color;
                    Console.Write(Tiles[j, i].ImageCharacter);
                }
                Console.WriteLine();
            }
        }
    }

10.Now underneath add the following code for the Tile,Wall and Sword classes

public class Tile
    {
        public string name { get; set; }
        public string ImageCharacter { get; set; }
        public ConsoleColor Color { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public Tile() { }

        public Tile(int x, int y)
            : base()
        {
            this.X = x;
            this.Y = y;
            ImageCharacter = Constants.TileImage;
            Color = Constants.TileColor;
        }
    }

    public class Wall : Tile
    {
        public Wall(int x, int y)
            : base(x, y)
        {
            ImageCharacter = Constants.WallImage;
            this.Color = Constants.WallColor;
        }
    }

    public class Sword : Tile
    {
        public Sword(Point p)
        {
            ImageCharacter = Constants.SwordImage;
            this.Color = Constants.SwordColor;
            X = p.X;
            Y = p.Y;
        }
    }

11.Now add the classes you need for the various creatures

 public class Creature : Tile
    {
        public int Hits { get; set; }

        public void Die()
        {
            Hits = 0;
        }
    }

    public class Player : Creature
    {
        public Player(Point p)
        {
            ImageCharacter = Constants.PlayerImage;
            Color = Constants.PlayerColor;
            Inventory = new List();
            X = p.X;
            Y = p.Y;
            Hits = Constants.StartingHitPoints;
        }

        public List Inventory { get; set; }
    }

    public class Monster : Creature
    {
        public Monster(Point p)
        {
            ImageCharacter = Constants.MonsterImage;
            Color = Constants.MonsterColor;
            X = p.X;
            Y = p.Y;
            Hits = Constants.StartingHitPoints;
        }
    }

12.Now add the following class for all our constants

 public static class Constants
    {
        public readonly static int DungeonHeight = 20;
        public readonly static int DungeonWidth = 20;
        public readonly static int NumberOfSwords = 5;
        public readonly static int MonsterDamage = 2;
        public readonly static int NumberOfMonsters = 1;
        public readonly static int StartingHitPoints = 10;

        public readonly static string TileImage = ".";
        public readonly static string WallImage = "#";
        public readonly static string PlayerImage = "@";
        public readonly static string SwordImage = "s";
        public readonly static string StepsImage = "S";
        public readonly static string MonsterImage = "M";
        public readonly static string CursorImage = ">";

        public readonly static ConsoleColor MonsterColor = ConsoleColor.Blue;
        public readonly static ConsoleColor PlayerColor = ConsoleColor.Gray;
        public readonly static ConsoleColor WallColor = ConsoleColor.DarkCyan;
        public readonly static ConsoleColor SwordColor = ConsoleColor.Yellow;
        public readonly static ConsoleColor TileColor = ConsoleColor.White;

        public readonly static string InvalidCommandText = "That is not a valid command";
        public readonly static string OKCommandText = "OK";
        public readonly static string InvalidMoveText = "That is not a valid move";
        public readonly static string IntroductionText = "Welcome to the dungeon - grab a sword kill the monster(s) win the game";
        public readonly static string PlayerWinsText = "Player kills monster and wins";
        public readonly static string MonsterWinsText = "Monster kills player and wins";
        public readonly static string NoHelpText = "No help text";
    }

13. Compile and Run this and you should see something like:

ReallyReallyReallySimpleRougueLike image

Screenshot of ReallyReallyReallySimpleRougueLike

Next Time
Next time I will present the first iteration of the program and we’ll see some nice improvements.

Advertisement

6 thoughts on “Building the Really Really Really Simple RogueLike v0.1 with C#

  1. Pingback: Vote for RSSSRoguelike for Roguelike game of the year | Dom's Code

  2. Hello Dom.. I can’t get this to work. I get like 35 errors. Am I doing it right with having the Program.cs and a Dungeon.cs with everything else in it? I’m using visual studio 2012.

    • Hi Luis sorry for the late response to your question – if you describe some of the errors I can try to reproduce but VS2012 should be fine. This project has been extended much further here if you are interested : http://rrrsroguelike.codeplex.com

      That will definitely build and its a lot more functional than the prototype used in the tutorial and you can even join the team if you are keen…
      Thanks
      Dom

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s