Build a C#/WPF RPG

This is a new WPF/XAML game, based on the game from my Windows Form tutorial: Learn C# by Building a Simple RPG

 

For the latest version of the complete solution, visit:

https://github.com/ScottLilly/SOSCSRPG

 

Overview

Lesson 00.1: Overview

 

Preparation

Lesson 01.1: Installing Visual Studio Community 2015 – Update 3

Lesson 01.2: Installing TortoiseSVN and VisualSVN

 

Planning

Lesson 02.1: Planning the Game

 

Programming

Lesson 03.1: Creating the Solution and Game Screen

Lesson 03.2: Creating and Using a Subversion Repository

Lesson 03.3: Creating the Player Class

Lesson 03.4: Creating the GameSession (ViewModel) Class

Lesson 03.5: Displaying the Player Object

Lesson 03.6: Update Player data with the PropertyChanged event

Lesson 04.1: Creating the Location class

Lesson 04.2: Creating the World

Lesson 04.3: Moving in the game world

Lesson 04.4: Improving the World – Inheritance and INotifyPropertyChanged

Lesson 04.5: Improving the World – Factory and Guard Clauses

Lesson 05.1: Creating the Game Item Factory

Lesson 05.2: Creating the Player Inventory

Lesson 06.1: Creating the Quest Factory

Lesson 06.2: Using Quests in the Game

Lesson 07.1: Creating Monsters

Lesson 07.2: Adding Monsters to Locations

Lesson 07.3: Sending Messages from the ViewModel to the View

Lesson 07.4: Monster Combat

Lesson 07.5: Monster and Combat Refactoring

Lesson 08.1: Completing Quests

Lesson 09.1: Creating Traders

Lesson 09.2: Adding the Trade Screen

 

Cleanup/refactoring, before adding more features

Lesson 10.1: Refactoring Base Class for Player, Monster, and Trader

Lesson 10.2: Grouping GameItems in Inventories

Lesson 10.3: Refactoring – Encapsulating LivingEntity Properties (Hit Points and Gold)

Lesson 10.4: Bug Fix – Removing multiple items from GroupedInventory

Lesson 10.5: Encapsulating Level and ExperiencePoints Properties

Lesson 10.6: Clean up property setters and PropertyChanged notifications

 

Create a base for automated tests

Lesson 11.1: Creating the Unit Test Project

 

New game features

Lesson 12.1: Making the GameItem class more flexible

Lesson 12.2: Creating the AttackWithWeapon command

Lesson 12.3: Making the Action class more flexible with an interface

Lesson 12.4: Letting the Monster use AttackWithWeapon

Lesson 12.5: Creating the first consumable GameItem

Lesson 12.6: Refactoring after adding Actions

Lesson 12.7: Creating recipes

Lesson 12.8: Crafting items with recipes

Lesson 13.1: Add keyboard input for actions, using delegates

Lesson 13.2: More keyboard actions (and fixes)

 

Load game world objects from XML files

Lesson 14.1: Moving game data to external files

Lesson 14.2: Creating extension methods

Lesson 14.3: Read World (Location) data from an XML file

 

 

Future lessons (exact content and order may change)

  • Load world data from XML file
    • Monsters
    • Quests, Recipes, and Traders
  • Use Aspect-Oriented Programming for PropertyChanged code
  • UI changes
    • RichTextBox fix
    • Tooltips for quests and recipes
    • DataTemplate for Player data
    • Progress bar for health (and experience?)
  • Save/load player game data
  • Complex battle logic

91 thoughts on “Build a C#/WPF RPG

    1. Thank you. Are you asking about the ability to load the game data (items, locations, monsters, etc.) from a file or database? If so, I think I can do that soon. I updated the future lessons list for what I have planned.

      1. not exactly im more talking about that i would store a city etc in a world entity
        so im am searching for a way to connect the locations from 2 different worlds.
        so that u can “walk” from one location to another in different worlds.
        though i have writen a connector class to connect the locations i m searching for another solution due to i have to add the connector manually every time i create a new world, wich i want to create with a world creator.

        1. The first idea I have is to create a new City class that has a list of Locations. Then, the World class holds a list of cities – and does not store Locations. The GameSession class would need another property for CurrentCity. If you have a “Travel to new city” button, that would show all the City objects in the World class. The City class would probably also need a “StartingLocation” property, for when a player first goes to that city. The World class would porbably also need a “StartingCity” property, for when a player starts a new game.

          There is probably more to consider. But, that is how I would start.

          1. To help out with Marco question:
            Why not have another location before the x,y coordinates?
            Then you can have 0 – Current map, x, y?
            This will allow to add more locations up and down.

          2. You could definitely do that. If you wanted to add locations above and below (a third dimension of travel), you could add a “Z” coordinate property to the Location class. Or, if you want to have cities, you could add a CityID property. If you do cities, I would probably add a “Cities” SortedList or Dictionary static property to the World class. It would have an integer as its key (for the CityID), and a string for the value (to hold the city name). That property would be useful when you want to associate the ID with the city name for the UI.

  1. Hi Scott. The walkthrough is really helping. Thanks! Going back a bit to the creation of the Quest Factory, you set it up so that itemsToComplete and rewardItems are set references for quests. I’m assuming you mean to change that so that any new quests will be able to have different quest items and rewards. I was thinking of putting it in an array, but I don’t want my code to diverge too far from yours. 🙂 Now that you are back to making updates I’m wondering if that could be in a future refactoring session?

    1. You’re welcome.

      Yes, to make additional quests, you would need to do itemsToComplete.Clear() and rewardItems.Clear() before populating those variables for the next Quest. Or, you could use some other technique to populate the Quest’s ItemsToComplete and RewardItems properties. In the future, all these factories will be changed to load from data files, or a database. But, I want to make sure the models are close to finished, so we don’t need to constantly update the data files to match the changed model classes.

      It should be safe to modify the factories to instantiate more locations, monsters, quests, etc. I don’t think any of the changes should affect the factories too much, until the big change to load them from data fiels.

  2. After the last lesson 10.3, I’m only seeing 1 of the quest items being removed instead of all 5 (it was working previously). I replaced my code with yours for the quests in case I I accidentally made some change, but there is no difference. It’s like the foreach loop is only running once. Any suggestions on where to look?

    1. You are correct. It looks like I have a bug in the RemoveItemFromInventory function, when subtracting quantities in the GroupedInventory property. I’ll make a bug-fix video. But, if you want to fix it now, I think you can change lines 147-148 in LivingEntity.cs to:

      GroupedInventoryItem groupedInventoryItemToRemove =
      GroupedInventory.FirstOrDefault(gi => gi.Item.ItemTypeID == item.ItemTypeID);

  3. Scott,
    I really enjoyed going through this with you. Is it possible you can post the completed code? I think I have an error, particular, the Quantity is not increasing. Strange is that the TraderScreen shows correct amounts.

  4. Hi Scott,

    I’ve been working on expanding the program with minor things like implementing additional attributes and weapons. I noticed the money system is very simple and breaks quickly when adding more expensive items. Instead of having a 5000 Gold Greataxe, I was wondering if you had thought about expanding to a copper/silver/gold/platinum system. A couple coppers for a rat tooth sounds more likely that a couple gold.

    I found a nice looking RPG currency System at https://pastebin.com/sqVjZYry

    Mickey

    1. You could do that. I would probably do something a little simpler. I’d create an extension method (let’s call it ToFormattedMoney) to format the integer property value as “1p 12g 75s 33c” – unless you want to force the player to go to a bank and convert their coins from 100 copper into 1 silver (for example). Then, I’d create new expression-bodied properties like “public string FormattedPrice => Price.ToFormattedMoney();”, and bind to those properties in the UI.

  5. Hi, first off great tutorials. You really deserve more publicity.
    I just wanted to ask how would one implement another wpf window for description. See I added a button instead of the description textblock that opens a new window with all the description and information about the location.
    I have several problems:
    1. I create an instance of the other window then use otherwindow.show() to call it. How can I make it so the main window is unusable while the other window is opened?
    2. When I open the second window nothing shows. Even though I have added a TextBlock

    It should work, it’s literally the same thing as if I were to call it in the mainwindow but it doesnt.

    1. Thanks.

      1. You can use ShowDialog(), instead of Show(), to prevent the main window from being usable. This is the difference between a “modal” and “non-modal” dialog.
      2. Did you pass the GameSession object to the second form, and set it to the DataContext?

  6. Thanks! ShowDialog() works perfectly!
    I forgot about that, though now that I have done that, the second window does show the description, only for the first location though. Doesn’t update whenever I switch locations. Must I have an OnPropertyChange somewhere to update the window every time I switch locations?

    1. It’s probably not updating because the Description property doesn’t raise a PropertyChanged event, and that is the property you are binding to. The CurrentLocation property of GameSession does raise PropertyChanged. So, you could subscribe to its PropertyChanged event in the new window, and update the TextBlock when that event is raised.

      This will probably require another change – setting a “x:Name” attribute on the textbox, so you can access it from the PropertyChanged event-handling function.

  7. I’m really sorry for bothering you again, though I still need further explanation.
    I understand what you’re saying (besides the fact as to why do we have to do this for a new window when everything works fine in the main one, and as far as I can see there is nothing separating those two windows code-wise) though I am still at a low-moderate level of coding and I just don’t know how to do the things you’re saying.
    How would I subscribe the Description to the CurrentLocation PropertyChanged event?
    Would I have to create a new event method like the one we did for GameMessages in the Description.xaml.cs then another EventArg?

    1. Because you have some additional changes to your code, can you upload your solution (including the directories under it, and all the files in those directories) to GitHub or Dropbox, so I can look at it?

      I’d like to take a look at it before I start giving you exact code to use.

        1. Here are new versions of Description.xaml.cs and MainWindow.xaml.cs that should fix the problem: https://gist.github.com/ScottLilly/a35e76aeb82cb0ca751d332bf072023e

          There were two problems that needed to be fixed.

          First, the Description constructor was instantiating a new GameSession object, and setting that new object to the DataContext. That new GameSession object always had the CurrentLocation set to the default location (0, 0).
          Second, the OnClick_OpenDescription() function in MainWindow set the description window’s DataContext to MainWindow’s _gameSession after the Descpryion window was displayed with ShowDialog. So, the Description window always showed the CurrentLocation of the new GameSession object it created in its constructor – not the _gameSession object from MainWindow.

          To fix the problem, I removed the new GameSession instantiation code from Description, and switched the order of lines 67 and 68 in MainWindow (so the Description DataContext gets set to _gameSession before displaying the window).

          Let me know if you still have questions.

  8. Oh snap, I’ve been trying to figure it out for days now. Works perfectly now and I fully understood what the problem is. Thanks!

  9. Hi Scott,

    I’m working on the Unit Test Project but I have no option to click on Run Unit Test from the Solution Explorer menu. Are you using an optional plugin that isn’t included with Visual Studio 2017 Community?

    Thanks.

    1. That might be part of ReSharper. I’ve had it so long, I can’t remember what is part of ReSharper, and what is part of Visual Studio.

      In the menu at the top of Visual Studio, do you see a “Test” option? If so, you should be able to run the tests through there.

  10. I’m having issues with the level not incrementing, everything else (XP is going up and Hit Points are going down (up when I eat a granola bar). Can you point me in the right diretion?

  11. Hi Scott, sorry for the late response, I replaced all my code with your, it works now. Thank you for you help. By the way, welcome back!

  12. Anyway to generate a UML of the RPG by chance? It would nice to have a visual of what is associating with what. Loving the videos!

    1. Visual Studio Ultimate (the paid version) has some architecture tools that can create UML diagrams. I’ll see if I can find a free method – besides manually creating the diagrams.

      1. Scott I think you can pull it in to the community edition by adding the component under the code tools section where you modify Visual studio. I’ve got 2017 community edition and I was able to add the class diagrammer.

    1. I hadn’t planned on that. Some of the early lessons are video-only. So, they would need to be written out before creating PDF versions. I’ll look into what it would take to do this, but that probably won’t be until October (at the earliest). I’m starting a huge project in two weeks.

  13. Hey Scott, is there a way to receive notifications or email from your web site whenever you post a new lesson, when comments are posted or basically any new content on the site?

    1. Hi Gary,

      I added the ability to subscribe to comments and new posts – look at the bottom of the comment entry form. For new posts, you can also follow my Twitter account at: https://twitter.com/ScottLilly. I always tweet when I publish a new post, along with the occasional tweet about other things C# programmers might find useful (no politics or anything else).

      Let me know if you subscribe and it has any problems.

  14. Thanks Scott.  I’ve been hoping to set something like that up!  I’ll subscribe and I’ll also follow you on twitter!

Leave a Reply

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