Howdy all,
As promised (somewhat belatedly!) I’m going to undertake a small coding project that, hopefully anyway, fits around my current commitments and the incredible weight of jobs I need to do before my impending wedding.
The plan, in the long term, will be to revisit Unity 3D as well as to continue the journey and forays into other technologies that I’m less familiar with. For now however, I just fancy (whether the results are good or bad) coding.
To that end I’m going to do a series of posts on creating an RPG/Adventure (classic D&D dice roller) packaged into a simple console application.
The snapshot below shows a set of stub classes, interfaces and other utility files that I’ve mocked up to start development on the project. This is all subject to change of course but I always find it relatively helpful to plan ahead and start drawing up interfaces and base classes upfront. These elements can always be removed later if it is discovered that there is a touch of over-engineering going on, therefore leading to a need to tighten the reins on a project and move to a simpler model. All of the code is presently tucked into the application project; I’ve opted to create no additional class libraries at this time (unless some interesting, reusable code comes out of this that I want to keep under source control for later use).

Most of the files are currently empty with the exception of some static utility methods and the following code in the core GameManager class.
using System; using System.Collections.Generic; using System.Diagnostics; using EpicQuest.GameEnums; using EpicQuest.Models; using EpicQuest.Utility; namespace EpicQuest.Manager { /// <summary> /// Epic Quest core GameManager class /// that handles general game state. /// </summary> internal class GameManager { #region Private Data Fields //Private List representing the party members involved with this adventure private List<Hero> partyMembers = new List<Hero>(); #endregion Private Data Fields #region Main Game Loop Method /// <summary> /// Main Game Loop 'Start' method /// called from the Main entry point method of the /// Program class (for this executable). /// </summary> internal void RunMainGameLoop() { //Debug information (method entered) Debug.WriteLine(Utils.MethodDebugMsg()); //Game startup initialisation logic GameStartup(); //Always run the game loop at least once (currently, break when user types EXIT) do { //Start game message Console.WriteLine(); Console.Write("Do you want to venture into the dungeon? Type EXIT and hit ENTER to flee in terror or simply press ENTER to continue: "); //Does the user want to quit... if (Utils.InterpretUserInput(Console.ReadLine()) == GameAction.ExitGame) { break; } //Clear the current game state (i.e. Party Members) ClearGameState(); //Create a new Party of heroes CreateQuestParty(); } while (true); //RunMainGameLoop method exit (confirmed via the output window) Debug.WriteLine(Utils.MethodDebugMsg(DebugMsgType.MethodLeft)); } #endregion Main Game Loop Method #region Private GameManager Helper Methods /// <summary> /// Private helper method that contains all of the Epic /// Quest game initialisation logic (on start). /// </summary> private void GameStartup() { //Write out a formatted string (in a red text colour) that represents the Games Title Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(Utils.WriteGameTitle()); //Reset the console to it's default colour scheme Console.ResetColor(); } /// <summary> /// Private helper method that can be called to clear /// down the Epic Quest game state. /// </summary> private void ClearGameState() { partyMembers.Clear(); } /// <summary> /// Private helper method (to be refactored/potentially moved) that /// allows the creation of a Party of heroes (currently allows 3 members). /// </summary> private void CreateQuestParty() { //Input help information - print to the console Console.WriteLine(); Console.WriteLine("Create your Party (BRW = Brawler, CLR = Cleric, MAG = Mage, NEC = Necromancer, THF = Thief)..."); //Loop three times and allow the creation of the relevant heroes for (int i = 0; i < 3; i++) { //Create a new hero (based on user selection) Console.WriteLine(); Console.Write("Choose your hero number {0}: ", i); Hero newHero = null; switch (Utils.InterpretUserInput(Console.ReadLine())) { case GameAction.ClericChosen: { newHero = new Cleric(); } break; case GameAction.MageChosen: { newHero = new Mage(); } break; case GameAction.NecromancerChosen: { newHero = new Necromancer(); } break; case GameAction.ThiefChosen: { newHero = new Thief(); } break; case GameAction.BrawlerChosen: default: { newHero = new Brawler(); } break; } //Safety check - Only add the hero if it's set correctly if (newHero != null) { partyMembers.Add(newHero); Console.WriteLine("{0} added to the Party.", newHero.GetType().Name); } } } #endregion Private GameManager Helper Methods } }
Nothing overly ‘classy’ (bad pun, sorry!) here to kick us off. I just wanted a stub class with the main game loop placed in to give us a template. The few take away points here are that all of the hero classes (Brawler, Cleric, Mage, etc) all feed off a, currently very lightweight, ‘Hero’ base class. A simple List of type Hero is serving as a way to keep track of the different adventurers as of this iteration; this is stored within the GameManager which may well change.
A very useful language feature worth mentioning here, which comes in especially handy when programming WPF applications and working with the MVVW design pattern, is the CallerMemberName attribute. In this case I’m using it as a debug mechanism via a call to Utils.MethodDebugMsg. This attribute essentially exposes a very simple way to gain access to the calling method/properties name in a called method (without using the archaic StackFrame class).
/// <summary> /// Internal static utility method that consumes a string method name and /// formats it as a short debug message (denoting when we enter or exit a method). /// </summary> /// <param name="msgType">The message type (have we entered or exited a method for example) as a enum. Defaulted to DebugMsgType.MethodEntered.</param> /// <param name="methodName">The calling method name as a string</param> /// <returns>A formatted string representing the method called (depending on the debug message type).</returns> internal static string MethodDebugMsg(DebugMsgType msgType = DebugMsgType.MethodEntered, [CallerMemberName] string methodName = "") { //If a valid method name is not provided simply return an empty string if (string.IsNullOrEmpty(methodName)) { return string.Empty; } //Use the DebugMsgType provided to return a formatted string (Have we entered/exited the given method name). Default = string.Empty switch (msgType) { case DebugMsgType.MethodEntered: { return string.Format("Entered the {0} method.", methodName); } case DebugMsgType.MethodLeft: { return string.Format("Exited {0} method.", methodName); } default: { return string.Empty; } } }
CallerMemberNameAttribute MSDN Reference
As far as architecture goes this is pretty much it for the time being.
Concept wise, I envisage a game with the following mechanics:
- 5 Hero classes with different skills and attributes.
- A ‘Dice’ rolling system supporting 3 different types of die (Green, Blue and Red).
- The concept of attack and defence dice (anyone remember Hero Quest!? Would like a spin-off of that).
- Various monsters to take on.
- Various weapons, armour and items to interact with and the ability to sell and purchase (via a simple vendor system).
- Basic sounds plugged in to accompany the game play.
Running the application, as it stands (with a few utility methods firing that I haven’t shown here yet), gives the following output in the console window:

You can tell you’re a developer when everything is zero indexed (hero number 0 come on down!), I probably need to change that!
As usual, comments and thoughts welcome; drop me a line if anything crosses your mind. As a little muck around, and as I’m still getting a feel for the various word press features available (the dream being to chuck some money at this later in the year), I’ve included a poll here to allow readers to pick a monster to be included in the game. Push buttons! You know you want to. Cheers all.