Lesson 18.1 – Future enhancements for the game

Congratulations!

You finished with the lessons, and have a working game!

Plus, you’ve learned many of the most common things you need to write more C# programs.

There is still plenty more to learn, if you decide to get serious about programming. I’ve been programming for over 30 years, and learn something new every week.

 

Expanding the game

The easiest thing for you to do is to make a bigger world, with more locations, quests, monsters, and items.

Draw a map of your larger world, and modify the World class to include these new locations. Create more monsters and quests. Add more powerful weapons, so the player can defeat the giant spider.

 

Ideas for new features

This is a very simple RPG, and there is a lot you can do to expand it.

Here are a few ideas:

  • Save the player’s current game to disk, and re-load it later
  • As the player gains experience, increase their level
    • Increase MaximumHitPoints with each new level
    • Add a minimum level requirement for some items
    • Add a minimum level requirement for some locations
  • Add randomization to battles
    • Determine if the player hits the monster
    • Determine if the monster hits the player
  • Add player attributes (strength, dexterity, etc.)
    • Use attributes in battle: who attacks first, amount of damage, etc.
  • Add armor and jewelry
    • Makes it more difficult for the monster to hit the player
    • Has special benefits: increased chance to hit, increased damage, etc.
  • Add crafting skills the player can acquire
  • Add crafting recipes the player use
    • Require the appropriate skill
    • Requires components (inventory items)
  • Make some quests repeatable
  • Make quest chains (player must complete “Quest A” before they can receive “Quest B”)
  • Add magic scrolls
  • Add spells
    • Level requirements for the spells
    • Spells require components to cast (maybe?)
  • Add more potions
    • More powerful healing potions
    • Potions to improve player’s “to hit” chances, or damage
  • Add poisons to use in battle
  • Add pets
    • Help the player in battle by attacking opponents
    • Help the player in battle by healing the player
  • Add stores/vendors
    • Player can sell useless items and buy new equipment, scrolls, potions, poisons, and crafting/spell components

 

There are also more programming techniques you can learn to make the program a little cleaner.

  • LINQ, when searching lists
  • Events/delegates, to handle communication between the “logic” project and the UI project – which will let you move more logic code out of the UI project
  • BindingList, so you don’t have to repeatedly repopulate the DataGridViews and ComboBox in the UI

 

Version control

If you’re going to make more changes, or write more programs, you really should learn how to use a version control tool.

You can create a backup copy of your program by copying the solution folder to a new location before making your change. But version control software is a better solution.

It will let you keep track of all the changes you ever make to your program. This is extremely helpful when you make some changes that don’t work, and want to go back to the old, working version.

I use TortoiseSVN (Subversion) and VisualSVN (a Visual Studio plug-in that works with TortoiseSVN).

Git is another popular version control tool. Many programmers use a web-based version of it at GitHub.

Version control tools usually take a little while to set up, and to figure out how to use. But once you have one in place, and you learn the basics, using it will become a habit that doesn’t require any time or thought. And the first time you need to go back to a previous version, you’ll thank yourself that you used version control.

 

Summary

Now that you have the basic game, you can expand it.

Hopefully you enjoyed these lessons, and learned some new things.

Please let me know if you have any questions about anything that wasn’t clear in the lessons, if you want to see some other features in the game, or if you want to learn some other aspects of programming in C#.

 

Next lesson: Lesson 19.1 – Scroll to the bottom of a rich text box

Previous lesson: Lesson 17.1 – Running the game on another computer

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

80 thoughts on “Lesson 18.1 – Future enhancements for the game

  1. Hi again Scott,

    I was wondering if I wanted to change the form’s background image depending on what location the player is currently at, how would I go about that, if it’s even possible?

    I’d assume you would have to add a new property to the Location class and its’ class constructor but I not sure how to program it.

    Then there is also the fact, would I have to individually import the images in the World class and then have it included in creation the locations in the world.

    Anyways, I’m not really sure, but here are my thoughts, please let me know if I’m not making myself clear enough.

    Thanks
    -Taryn

    1. Hi Taryn,

      Here is some information on how to set the background image for a Windows Form: https://stackoverflow.com/questions/20045676/how-to-change-background-image-of-a-form-in-c It tells how to change the image based on the user selecting a menu option. But, you can change that to when the player is at a new location.

      Here are some lessons about adding images to SuperAdventure (this version, and a WPF version):
      Lesson 26.1 shows how to include images in a project.
      Lesson 04.1 and lesson 04.2 (of the WPF version of this game) show how to modify the Location class, so you know which image to display. You won’t be able to paste that code into your version of the game, but it should give you an idea how it works.

      Let me know if you try it and have any problems.

  2. Hi again Scott,

    So I was able to successfully modify the location class and the locations themselves but I don’t know how to get the string image path to an actual image to be able to set it as the background image.

    I’ve tried adding

    //Set the background image of the form
    this.BackgroundImage = newLocation.ImageName;

    And then I tried

    Image myImage = new Bitmap(newLocation.ImageName);
    this.BackgroundImage = myImage;

    And it didn’t bring up an immediate error but when I ran the code it came up with an error that states “An unhandled exception of type ‘System.ArgumentException’ occurred in System.Drawing.dll” If you have an idea of what I should or could do, that’d be most appreciated.

    Thanks a lot,
    -Taryn

    1. Do all locations have a value for their ImageName property?
      What values are you putting into the ImageName property? Do they include the namespace, like “/Engine;component/Images/Locations/{imageName}” (including the image file’s extension, like “png”)?
      Are the images included in the project, and are their “Build Action”s set to “Resource”?

  3. Hello Scott. Thanks for your tutorials.

    However, I ran into a problem when I start my program. As I wanna choose a sword, it stands as Engine.Weapon. When I click it, it crashes.

    1. Hi Anders,

      In SuperAdventure.cs, do you have these lines of code:
      cboWeapons.DataSource = _player.Weapons;
      cboWeapons.DisplayMember = "Name";
      cboWeapons.ValueMember = "Id";

      If that is correct, 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?

        1. I found the problem. It is with some “eventhandlers” – connections between UI controls (buttons, comboboxes, etc.) and the code in SuperAdventure.cs. This can happen if you accidentally double-click on the UI control, when you are in the form designer.

          The program should work if you update SuperAdventure.cs and SuperAdventure.Designer.cs with the files here: https://gist.github.com/ScottLilly/4f0972b670c74be6104a008a4bb6345f.

          I commented-out the extra eventhandlers (and their functions) for cboPotions_SelectedIndexChanged and cboWeapons_SelectedIndexChanged. I also connected btnUseWeapon and btnUsePotion to their eventhandlers (btnUseWeapon_Click and btnUsePotion_Click).

          You can learn more about eventhandlers in Lesson 21.3

          Please tell me if that does not fix the problem, or if you have questions about it.

  4. Hey, Scott, I am trying to add a shop to my game where the player can buy stronger weapons and things like that, but I want to, in order to keep the UI organized, have the items the character can buy as a combobox and button, I think I’m having trouble assigning an Item called _buyItem variable in SuperAdventure.cs, because I can use the variable but it doesn’t function properly. I think the issue is on line 205 of SuperAdventure.cs. I’m using the combobox cboShopItems to assign it, once at the end of MoveTo() at line 205, and in cboShopItems_SelectedIndexChanged(object sender, EventArgs e) on line 496, but neither work. _buyItem is supposed to hold the item that is selected by the cboShopItems so that the buy button (btnBuyItem) will be updated with the cost, and when they hit the buy button, it will be added to their inventory. The cost always displays as 0, and the item is not added to the inventory. Here is my code on GitHub: https://github.com/JenniferE55/SuperAdventure/tree/NewLocationShop

    Can you help me?

    1. Hi Jennifer,

      In the constructor of the Item class, there is an “int” in front of the “Value = value;” line. The code thinks you want a new “Value” variable, and it assigns the parameter to that new variable, instead of the Value property. So, all the Item objects currently have a value of “0”. If you remove the “int”, the rest of your code should work.

  5. Hello Scott,

    I’m working around with the code to understand it better and trying to expand the game by adding the simplest stuff to it (e.g. location, quest, etc). But I’m having difficulties with the quest part.

    For example, you received the quest at ‘Point A’ but I want it to be completed at ‘Point B’, I’ve tried various method like adding ‘if and else’.

    Not only did it cause the code to have more lines and be more messy, it does not really work as well.. It either return me an error about my poor written code, or I still have to complete the quest at ‘Point A’ so I have reverted all the code back to the tutorial code and went back to square one.

    Any suggestion on what I can do?

    1. I would start by adding a new property to the Quest class – an integer property named CompletionLocationID. Add a parameter to the Quest class, so you can pass in a value when you instantiate a Quest object, and have the constructor set the property value from the parameter.

      In SuperAdventure’s MoveTo function, I’d add some new code between the section for “// Does the location have a quest?” and “// Does the location have a monster?”. This code would loop through the player’s Quest list, looking at each PlayerQuest object that has not been completed and run code like what is in the section “// If the player has not completed the quest yet” – except every place that uses “newLocation.QuestAvailableHere” will need to use the Quest object from the current PlayerQuest from the loop. This will create some duplicate code (really, “almost-duplicate”), which should probably be moved to a separate function where you can pass in the quest object – either newLocation.QuestAvailableHere or the Quest from the PlayerQuest object. But, if you are going to do the additional lessons beyond 18.1, you might want to wait until you finish all of them. There are changes in the future lessons that could overwrite your changes.

      If you try this idea and have any problems, please let me know.

      1. Hello Scott,

        Thanks for the quick replies and suggestions! For the additional lessons, I would prefer to do them by using your GitHub tutorial code so I won’t be very confused. As for what I’m doing now is to get a better understanding with the lesson I’ve gone through so far. (I can understand most of it now! But adding new thing is pretty hard..)

        I’ve spent lots of hours trying to get the Quest to work but to no avail.
        🙁

        These are what I’ve done so far, I’ve made changes to only those 3 files but most of the problem came from the code between “// Does the location have a quest?” and “// Does the location have a monster?”. There’s definitely something wrong with the code, could you help me take a look at what’s is wrong with it? (Line 155 onward in SuperAdventure.cs)

        (sorry I’m new to GitHub too, tell me if I missed out anything)
        https://github.com/QinYong12/SuperAdvQuest

        1. It looks like the Quest and World changes are good.

          Before making the changes below, I suggest you use the Visual Studio debugger. Set a breakpoint on line 156 and use F10 to step through the code. Hover over the variables to see how they are being set. You’ll notice that the loop at lines 158 to 167 looks at all the player’s Quest items, but only has one result for the playerAlreadyCompletedQuestTest (line 164). We want to check this for each PlayerQuest. So, the code for “does the player have all items to complete this quest” needs to be inside this loop, not in the code starting at line 170.

          In SuperAdventure.MoveTo(), the code needs to look more like this (I didn’t test these changes, so there might be some errors): https://gist.github.com/ScottLilly/fce984e543e19eea5984ef300caa8410

          Notice that the code to check if the player has all QuestCompletionItems is now inside the loop through the player’s Quests items. This way, we are doing this for all PlayerQuest objects that should be completed at the new location, and that have not already been completed.

          Let me know if this does not work for you, or if you have any questions. If you do have questions, I suggest first using the debugger to step through the code as it runs. That can help you see what is happening.

          1. Hello Scott,

            Thanks for the help! I’ve learned how to use the Debugger, even though I’m still not very good with it I can see the value changes. For my code, the value remained as ‘false’ for what I can see.. That explained one of the problem that other quests keep repeating and completing by itself whenever an action is called.

            As for your code, there were red underline with all the ‘playerQuest.Quest’ it took me awhile to figure out about it. I couldn’t do debugging because the program is unable to run, so I end up doing some trial and error and changed it to ‘playerQuest.Details’ because it was the one of the 2 property available after the ‘playerQuest.’ and it worked!

            I did not expect to place the entire code under 1 ‘foreach’, this has helped me learned a lot. There are still some “bug” like going back to the area where you received the quest still completes it (probably the newLocation.QuestAvailableHere part) which I will somehow try to solve it myself.

            Once again, thanks a lot for the help!!

  6. Hello Scott,

    Sorry for having multiple post but, how can I have multiple quest in one location as well as having the quest RewardItem to give more than one item? I feel like I need to use a List but I’m totally clueless on that T_T hope you can guide me!

    Thanks.

    1. Hello Qin,

      Yes, you would want to use a List. I’ll give you some suggestions for the RewardItems, and let you try making the change for multiple quests on your own. If you have trouble making the changes, let me know. But, I want you to try first. Figuring out how to solve (or fix) problems is an important part of programming.

      1. In the Quest class, delete the RewardItem property and create a new RewardItems property with a datatype of “List
      2. In the Quest constructor, initialize the RewardItems property to “new List
      ()”
      3. In the World class, you’ll need to change the lines that set the RewardItem property to a value. Instead, have them use RewardItems.Add(whateverItemYouWant)
      4. In the SuperAdventure MoveTo() function, when add the reward item to the player’s inventory, you’ll need to loop through the RewardItems property (probably using a “foreach”) and add each individual item to the player’s inventory.

      The multiple quests would be similar, except there are several places in the MoveTo function that expect QuestAvailableHere to be one object (or null). A quick solution is to surround any place that is looking at QuestAvailableHere with a “foreach” loop. Then, instead of using newLocation.QuestAvailableHere, you’ll use the current Quest object from the loop. If you need an example of the looping, check line 163-166 of SuperAdventure.cs, where items are added to the monster’s loot table.

      Let me know if you try that and have any questions.

  7. Hello Scott,

    Thank you for the step by step guide! I’ve only managed to add the items to the Inventory when the player already has the item in their inventory.

    I’m facing some difficulties in adding the items to their inventory when they do not have the item.

    I’ve done Step 1 to Step 3 and created a Class called RewardItem.cs with the same code as QuestCompletionItem.cs then I pretty much replicate the “foreach” to add it into the player’s Inventory. On (line 113) in SuperAdventure.cs the RewardItem was removed and replaced by a List, so there’s a red underline there. How do I make it work the way it is again?

    On (line 139), this is the main problem for me, I’ve really tried and really have no idea what code should be written there for it to work, I’ve tried a lot of dumb things but all of them have some kind of syntax error to it so it doesn’t work..

    https://github.com/QinYong12/SuperAdvMultipleReward

    1. For line 113 of SuperAdventure, to loop through all the RewardItem objects, you’ll need to add a “foreach” loop. So, it will look like this:
      foreach(RewardItem rewardItem in newLocation.QuestAvailableHere.RewardsItems)
      {
      rtbMessages.Text += rewardItem.Details.Name + Environment.NewLine;
      }

      For the code at line 139, there are a few things to do.
      1. On line 122, you have a loop for each reward item you try to give the player. You need to check if the player already has each item, when you try to give it to them. So, the loop at line 137 needs to be inside this loop. Otherwise, it will only try to add the last item (after the loop has reached the last item and finished running.
      2. You need to set “addedItemToPlayerInventoryTest” to false each time you try to add a RewardItem, because you need to check if the player already has each item. So, that line also needs to be inside the loop (but before the loop that checks each item in the player’s inventory – currently on line 124).
      3. If the player doesn’t already have the reward item in their inventory, look at line 26 for an example of how to add an item to their inventory (with a quantity).

      Also, in World.cs, it looks like lines 95 and 96 are duplicates.

      Let me know if you try these ideas and still have questions.

      1. Hello Scott,

        Thank you!

        Everything worked perfectly. Thank you for setting aside some time out of your busy schedule to explain the code to me and also providing me with the solution! I like how you made this guide years back and is still helping newcomers solving their problem with their code!

        For the World.cs, it was intended. I was testing out if those works and I got lazy so I copy pasted them instead.. It was my bad! But thanks for the notice 🙂

        1. You’re welcome! In many programming jobs, you don’t ever talk with the people who will use your work. It can make programmers feel disconnected. But, with these lessons, I get to hear when I help people from around the world. So, I am happy to help.

  8. Hi Scott, I learned a lot from your tutorials and had a lot of fun, and wanted to say thank you. I Wanted to add a new weapon when the player reaches the guard post but i cant figure out how to do it, i already made the weapon but how would i give it to the player without completing any quests or killing any monsters?

    1. Hi Hasan,

      I sent a reply to your email, but I’ll post the answer here too, in case someone else wants to do the same thing.

      To make the change you want, I would add a new property to the Location class – “public Item GiftItem { get; set; }”. You would set the value for this property when you create the Location object (in World.PopulateLocations). Then, inside the MoveTo() function, add some new code. If you only want to give the item to the player once, you could put it inside the “if(!LocationsVisited.Contains(CurrentLocation.ID))” – if you’ve done all the lessons up to 26.2. The code might look like this (I haven’t tested it, so there might be some mistakes):

      If(!LocationsVisited.Contains(CurrentLocation.ID))
      {
      LocationsVisited.Add(CurrentLocation.ID);
      If(CurrentLocation.GiftItem != null)
      {
      AddItemToInventory(location.GiftItem);
      RaiseMessage(“You receive a ” + location.GiftItem.Name);
      }
      }

      Or, you might want to give the item to the player if they don’t have it inside their inventory. Then you would make a separate if statement, like:

      If(CurrentLocation.GiftItem != null)
      {
      If(!Inventory.Any(ii => ii.Details.ID == location.GiftItem.ID))
      {
      AddItemToInventory(location.GiftItem);
      RaiseMessage(“You receive a ” + location.GiftItem.Name);
      }
      }

      Let me know if you try it and have any questions.

        1. You could add the second part now (the code that will give the player the item if they don’t have it in their inventory). But, a player could use that to get the item and sell it to a trader – so they could get another item to sell and repeat as many times as they want. You’ll have to wait until lesson 26.2 before we add the logic to know if it is the first time the player visited the location.

  9. i am having a number of problems such as the name currentlocation does not exist in the current context. Which is the same for location, inventory, additemtoinventory, raisemessage. I checked all my spelling and it seems to be right?

        1. I downloaded your version of the program. The code I originally gave you was for after you’ve completed all the lessons. So, some properties are in different classes and there are some new methods. Here is a version that should work with your earlier version of the program. If you do the rest of the lessons, you will eventually move the MoveTo function into the Player class. So, you will need to remove the “_player.” from the code below, because that code will be inside the Player class, and will not need to reference the “_player” variable.

          if(_player.CurrentLocation.GiftItem != null)
          {
          if(!_player.Inventory.Any(ii => ii.Details.ID == newLocation.GiftItem.ID))
          {
          _player.AddItemToInventory(newLocation.GiftItem);
          rtbMessages.Text += "You receive a " + newLocation.GiftItem.Name + Environment.NewLine;
          }
          }

          Let me know if that doesn’t work, or if you have any questions.

  10. Hello Scott Lilly,
    first of all I would like to thank you, the guide was very useful as a beginner.
    I kinda liked the occasional “theory” explanations about variables or functions and classes, I am still unsure on so many things but I guess experience only can make it better.

    I have done some changes in the code…primarily the player won’t heal itself every step, he can use healing potions out of combat, monsters actually “die” becoming NULL (because it gave me problems if they stayed there), a respawn function that keeps quests and items that allow to enter locations.

    I put the code here for everyone, but I want to ask you one thing. Is there anyway to recall function SuperAdventure() in respawn function? (respawn function is called ResetByDeath())…I set a new _player and I give it what it needs at the beginning, but I think that if the game were bigger and the code longer loading everything would be longer.

    Thanks for the attention, thank you again for the guide.

    Here’s the code (SuperAdventure.cs, the rest completely the same as yours until Lesson 19.2):
    https://pastebin.com/ypTamn9u

    1. Hello,

      You’re welcome! You are correct. When you write more programs, you will start to remember things and understand them better.

      Here is a suggestion for some changes: https://gist.github.com/ScottLilly/646fc4a9b39746b5031d018ecffd3367. It is not tested, and there might be some problems or typos. But, I think you might see what it is doing.

      There is a new function SetCurrentPlayer(), that accepts a Player object, stores it in the “_player” variable, gives it the default items (rusty sword and potion), moves the player to their home, and updates the UI controls. Call this function from the SuperAdventure constructor, with the beginning Player object. You can also call this function from the ResetByDeath function, passing in the “newPlayer” object.

      I think you could also change ResetByDeath() to only delete items from the “_player” variable’s inventory that are not needed to enter a location. You would not need to create the new “newPlayer” object.

      Let me know if you try it and have any questions. If you leave another comment, they do not appear automatically. I have this website setup so I need to approve all comments before they are displayed, because I have received over 15,000 comments that needed to be blocked or deleted.

      1. Hello,

        Thanks for the fast reply! I tried it out and works fine.
        Just missing the line ” newPlayer.CurrentHitPoints = _player.MaximumHitPoints; “,
        after creating the object newPlayer (for everyone following this code).

        I have no idea how not to create “newPlayer” object, since I would like to keep my experience and maximum hit points (they do not increase yet) of the previous player.

        Is creating a respawning function that recalls a “default-player” function, let’s say, a general rule to make a respawn function?

        Have a good day, and thank you again for the willingness.

        1. You’re welcome.

          Normally, a respawn function probably would not create a new player object. Instead, it would modify the existing player object. For example, move them to a location, reset their hit points, and maybe penalize them by removing some gold or experience points. If you think of the player object as a real person, you would find someone on the battle field, take them to the hospital, heal them, and charge them some gold. You probably would not leave them there, go to a cloning laboratory, and create a new person that looks like them.

          It’s the same idea for objects. I saw your other comment about the databinding problem, and will reply to it. It might be related to the “newPlayer” object.

Leave a Reply

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