Lesson 23.1 – Creating a console front-end for the game

Lesson Objectives

At the end of this lesson, you will know…

  • How to create a console (text) application, using the existing Engine project
  • How to do simple parsing of user input

 

In this lesson, we will create a new front-end for the SuperAdventure program. This one will be a “console application” – a program that only uses text in the user interface.

This will let us play SuperAdventure the same way as the classic adventure game, “Hunt the Wumpus“.

The great thing about creating this second interface is that we can re-use a lot of the code we already have, especially since most of the game logic is in the Engine project (and not the UI code).

 

Step 1: Add the console project

Open the SuperAdventure solution in Visual Studio. In the Solution Explorer, right-click on the SuperAdventure solution, and select “Add” -> “New Project…”. Choose “Console Application”, in the Visual C# section, and name it “SuperAdventureConsole”

This will create the new UI project.

Right-click on the SuperAdventureConsole project, and select “Set as Startup Project”.

After you do this, when you run your solution, it will run the console UI. If you want to run the Windows Form UI again, right-click on the SuperAdventure project, and select “Set as Startup Project”.

If you don’t want to switch the startup project for the solution, you could also right-click on the project you want to run (SuperAdventure or SuperAdventureConsole), and select “Debug” -> “Start new instance”. That will run the project you clicked on.

Right-click on “References”, in the SuperAdventureConsole project, and add the Engine project. That will let us use the game objects in this version of the game.

 

Step 2: Edit Program.cs, in the SuperAdventureConsole project.

This class has a static function named “Main”. This is the function, and class, that is executed when you run the console program.

We are going to put an “infinite loop” in this function.

Normally, an infinite loop is a bad thing in a program. It means your program is stuck doing something, and it will never end. However, we are going to add a way for the player to exit the loop – by typing the command “Exit”. If the user types anything else, the program will continue running, and the player can continue playing.

Inside our loop, we will wait for the user to type something and press the Enter key. When they press Enter, we will try to determine what the game should do, based on the user’s input.

 

Step 3: Paste the code below into Program.cs.

 

 

This is everything we need to run SuperAdventure as a console application. You will probably notice that some of the functionality is from the Windows Form project.

 

Line 11-13: We set the variable for the saved game file, and the player. Because the Main function is a static function, we need to make the _player variable static. Also, the functions that Main calls will need to be static too. These are basically the same as lines 18-20, in SuperAdventure.cs, in the Windows Form project.

 

Line 18: We call the LoadGameData function. That function looks for a previously-saved game, or creates a new player. This code is the same as lines 26-38, in SuperAdventure.cs.

 

Lines 20-21: Console.WriteLine is a function you use to display text in the console UI. This displays the text you pass in the parameter, and does a line-feed (moves to the next line down). If you don’t want to move to the next line, you would use:

 

Line 23: Calls a function that display’s the player’s current location. We put this in a function, so we can display the same text when the player moves to a new location.

 

Lines 25-27: We connect the events from the Player class to the functions that will handle them for the UI (similar to lines 96-97, of SuperAdventure.cs).

 

Line 30: This is our game loop. It will continuously run, waiting for the user’s input, until the user types “Exit”. A “while” loop evaluates the equation inside the parentheses, and runs until that equation is false.

However, we don’t have an equation in there. We only have “true”, which will always be true, which means the “while” will never end – until the player types the word “Exit”, and the code in the “if” on line 48 runs. The “break;” will exit the loop it is in, and finish running the rest of the code in the function. But, there is no code in this function, so the function will end – and the program will stop running.

 

Line 33: Display a “>”, as a prompt for the user to enter their command. Because we use “Console.Write()”, the cursor will stay on the same line as the prompt.

 

Line 36: Console.ReadLine() is the function to read the user’s input, after they press the Enter key. There is a Console.Read() that you could use to read every key pressed, one character at a time. But we are going to wait for the user to press Enter. Whatever the user types will be assigned to the userInput variable.

 

Lines 39-42: If the user only hit the Enter key, and didn’t type anything else, the userInput will be null. The “continue;” commands tells the program to go back up to the “while” line and keep running. It does not execute the lines after it (45-56).

 

Line 45: We convert the user’s command to all lower-case letters. So, if the user types “EXIT”, “Exit”, or “exit”, the value in cleanedInput will be “exit”. This will make it easier for us when we try to parse what the user typed, to determine the action to perform.

 

Lines 48-53: If the user typed “exit”, call the SaveGameData function and break out of the loop – ending the game.

 

Line 56: If we got here, the user typed a non-blank line, that was not the word “exit”. Now we will call the ParseInput() function, and try to do what the user wants. This will be a bug function, since we need to manage every action that we had buttons for in the Windows Form UI.

 

Lines 60-71: This is the function we call when the player’s PropertyChanged event is fired. It is similar to PlayerOnPropertyChanged, in SuperAdventure.cs. However, since we don’t have buttons to hide and show, or datagrids to update, it is much smaller.

The only thing we care about is if the player is at a new location. Then, we display the location’s information, and the vendor information (if there is a vendor at this location).

 

Lines 73-81: Display messages from the player’s events, like DisplayMessage() in SueprAdventure.cs.

 

Lines 83-392: This is where we look at the user’s input, and try to do what the action they want to perform. It’s a long series of “if”s and “else if”s. If we cannot determine what the user wants to do, we eventually reach the “else” at line 384 and give the user a message that we don’t understand their input, and they can type “Help” to see a list of the valid commands.

You should be familiar with much of the logic in this function, but I will explain the new things.

 

Line 85: The Contains() is a new function. You can use it to see if a string (“input”, on this line) contains the string used as the parameter (“help”, in this code).

We use this, because we want this “if” to run when the user types the word “help” anywhere in their input. So, if they type, “I need help”, or “HELP ME!!!!”, we will display the list of valid commands.

We also check if they typed “?”, a common way used in console app games to display the help information.

 

Line 107: If you want to create a string, with variable values inside it, you could do it with string concatenation – adding pieces of the string together, like this:

However, Console.WriteLine() and string.Format() have a little bit cleaner way to do this. You have the string you want, with {0} in the location where you want to include a variable value. After the string, you have the variable you want to insert into the string. The function is smart enough to automatically do a .ToString(), if it is needed.

If you want to put several variables in your string, you only need to add more sets of curly braces, and more variables to your list. Just remember that the position is important. The first variable in your list will go where {0} is. The second will go where {1} is, and so on. So, you could have something like this:

 

Lines 117-160: In the Windows Form version, we hide the movement buttons for directions where there is no location. We can’t do that in the console version, so we need to check if there is a valid location, before trying to move the player there. To be nice, we display a message if the location does not exist.

 

Lines 183-207: Similar to the movement validations, we need to check if there is a monster at the location, before we try to allow the player to “attack”. Because we don’t have a combobox, with the player’s weapons listed in it, we do a few checks, and try to pick a default weapon, when the player tries to attack the monster.

 

Lines 208-233: This is a more complex command. We are looking for the word “equip”, but we also need to know what weapon the player is trying to equip (set as their default weapon).

On line 208, we check if the player’s input had “equip ” – notice the space at the end. Then, we take the rest of the player’s input string, starting at the sixth position

NOTE: For functions like Substring, the first character of a string is at position 0, not position 1.

The Trim() function removes any leading or trailing spaces from a string.

So, if the user types “Equip rusty sword”, inputWeaponName will be “rusty sword”.

On line 218, we look through the player’s inventory and try to find an item with the same name as what the player is trying to equip. If it finds something, it will assign it to the “weaponToEquip” variable. If it does not find a matching weapon, that variable will be null (the default value).

If we don’t find the weapon, we display a failure message to the player. If we do find it, we set it to the player’s current weapon, and display a success message.

 

Lines 234-257: We do the same thing here, as we did for the equip weapon section, except we look for a potion to drink.

 

Lines 258-300: When the player inputs “trade”, we will show the player’s inventory and the vendor’s inventory, if there is a vendor at the player’s current location.

On line 260, we check how many items the player has that they can sell (the price does not match our “flag” price that indicates the item cannot be sold). If the player does not have any sellable items, we display a message. If they do, we go to line 275, loop through their inventory, and display each item with its price.

In lines 283-298, we do the same for the vendor’s inventory – except we don’t need to check for unsellable items.

 

Lines 301-345: This is where we try to handle the player buying an item from a vendor.

After making sure there is a vendor at the player’s current location, we do the same type of substring function that we did for equipping a weapon and drinking a potion. Then, we check if the vendor has that item, and if the player has enough gold to buy it. If so, we purchase it on lines 337-338, the same way we do on lines 167-170 of TradingScreen.cs.

 

Lines 346-382: Try to sell an item, using very similar parsing and logic as we did to buy an item.

 

Line 384: If the user’s input did not match any of our previous checks, we reach the final “else” and display our error message.

 

We have the DisplayCurrentLocation function in order to have a consistent format. We want to display this information from a couple different places. So, instead of duplicating code, we have a single function.

 

The LoadGameData function is the same as what we have in lines 26-38 of SuperAdventure.cs. The SaveGameData function is the same as the SuperAdventure_FormClosing function in SuperAdventure.cs.

 

NOTE: If you did not do the SQL lessons, you can delete, or comment out, lines 406 (where we try to load the saved game from the database) and 425 (where we try to save it to the database).

 

 

Check that your program works

Run the program and try typing in some commands. Type “Help”, if you forget the game’s commands.

 

 

Summary

Now, you have the same game, with two different front-ends.

The nice thing about having most of our game logic in the Engine class is that we can create this new front-end very quickly. I wrote all the code in Program.cs in less than two hours. In fact, it took me longer to write this lesson, than to write the code.

If you know XAML, you could probably create a WPF/XAML front-end project even faster. You would have similar UI controls (buttons, datagrids, etc.), and not need to figure out the user input parsing logic.

You could also create a unit test project, to do some automated testing of the game logic.

 

Source code for this lesson

Get it from GitHub: https://gist.github.com/ScottLilly/6f146d4079aaef92c251

Or DropBox: https://www.dropbox.com/sh/9dq51p8hcmbjeox/AAANZXfY7SHVQmh6vIoP8Abva?dl=0

 

Next lesson: Lesson 24.1 – Make the SuperAdventure source code easier to understand and modify

Previous lesson: Lesson 22.3 – Creating the SQL to save and load the saved game data

All lessons: Learn C# by Building a Simple RPG Index

3 thoughts on “Lesson 23.1 – Creating a console front-end for the game

  1. Hi Scott,

    I think I caught a minor error in the Main method. userInput after Console.ReadLine(); can never be null. If you enter nothing it will be String.Empty or “”. The only way for it to be null should be when it’s declared but un-initialised.

    Kind regards,

    J

  2. Obviously this doesn’t break the game because it will do the same (continue looping) except without entering the if (userInput == null) statement.

Leave a Reply

Your email address will not be published. Required fields are marked *