Lesson Objectives
At the end of this lesson, you will know…
- How to plan a function
- How to write the code/logic for a function
- Some of the most common C# commands
Ok, we’ve spent all this time building up the pieces we need for our game. Now it’s time to actually get the game to do something. The first thing we’re going to add the code to handle when the player moves to a new location.
By the way, this is a long lesson, and covers a lot of new things. So make sure you have some time to complete it.
Outline of the function
The first thing we need to do is figure out what’s going to happen during a move.
When I write a function that needs to do a lot of things, like this one, I like to do some planning first – so I don’t miss anything.
When the player moves to a new location, we’ll completely heal them, we’ll check to see if the location has any quests (and if the player can complete them), and if there are any monsters to fight there.
Here is an outline of the logic for a player move function. Something like this is often called “pseudo-code”. It isn’t C# code, but it represents what the code will do.
Each indentation level is where we handle a different condition – for example, the steps to follow if the location has a monster, and the steps to follow if the location doesn’t have a monster.
- If the location has an item required to enter it
- If the player does not have the item
- Display message
- Don’t let the player move here (stop processing the move)
- If the player does not have the item
- Update the player’s current location
- Display location name and description
- Show/hide the available movement buttons
- Completely heal the player (we assume they rested/healed while moving)
- Update hit points display in UI
- Does the location have a quest?
- If so, does the player already have the quest?
- If so, is the quest already completed?
- If not, does the player have the items to complete the quest?
- If so, complete the quest
- Display messages
- Remove quest completion items from inventory
- Give quest rewards
- Mark player’s quest as completed
- If so, complete the quest
- If not, does the player have the items to complete the quest?
- If not, give the player the quest
- Display message
- Add quest to player quest list
- If so, is the quest already completed?
- If so, does the player already have the quest?
- Is there a monster at the location?
- If so,
- Display message
- Spawn new monster to fight
- Display combat comboboxes and buttons
- Repopulate comboboxes, in case inventory changed
- If not
- Hide combat comboboxes and buttons
- If so,
- Refresh the player’s inventory in the UI – in case it changed
- Refresh the player’s quest list in the UI – in case it changed
- Refresh the cboWeapons ComboBox in the UI
- Refresh the cboPotions ComboBox in the UI
Creating a shared function
We have four different functions for movement, one for each direction. And we need to do the steps shown above for moving in each direction.
We could do that by writing the same code in each function. But then, if we ever want to change that logic, we’d need to make the change in four places – and that often leads to mistakes.
So, we’re going to create a new shared “MoveTo” function to handle movement to any location. Each of the four movement functions will call that one new function.
Storing the player’s location
We also need a place to save the player’s current location. Since this value will change, we need to store it in either a variable or a property.
In this case, we’ll make a property in the Player class. It makes sense to do this because a player’s location is a “property” (in the general sense) of the player.
Storing the current monster
We also need a place to store the current monster that the player is fighting, in case they move to a location that has a monster there.
In the World class, we have a list of all the monsters in the game. However, we can’t use the monsters from there to fight against. We only have one “instance” of each monster. So, if we fought against the rat in the World.Monsters list, and killed it, the next time we fight against it, it would already be dead.
When we move to a new location, if it has a monster there, we’ll create a new instance of that type of monster and save it to a variable. Then, the player will fight against that instance of the monster.
Now that we know what we want to accomplish, we’re ready to add the code.
Creating the functions to move the player
Step 1: Start Visual Studio and open the solution.
Step 2: First, let’s create the new property to store the player’s current location.
Double-click on the Player class, in the Engine project. Add a new property named CurrentLocation, with a datatype of Location.
public Location CurrentLocation { get; set; }
Note: You don’t have to put the properties in any specific order. I just like to like to keep my List properties at the end of the properties. I find it a little easier to read when they’re grouped in the same place in every class.
Step 3: Right-click on the SuperAdventure.cs form, in the SuperAdventure project, then select “View Code”.
Step 4: Because we have a lot of code to write, and it’s easy to mistype something, copy the new code for the SuperAdventure form from here (https://gist.github.com/ScottLilly/208630cfcdded1cbfdc0) and overwrite all the existing code in SuperAdventure.cs with it.
What’s in the code you just added?
On line 18, we added a new variable to hold the monster that the player is fighting at the current location.
In the form’s constructor, we do a couple things to start the game.
On line 25, we “move” the player to their home. Since the MoveTo function expects a location as the parameter, we need to use the World.GetLocationByID() function to get the correct location. This is where we use the constant World.LOCATION_ID_HOME, instead of using the value 1. It’s much easier to look at the code with the constant and know what it’s supposed to be doing when we use this clearly-named constant.
On line 26, we add an item to the player’s inventory – a rusty sword. They’ll need something to fight with when they encounter their first monster.
We added the new MoveTo function to handle all player movement.
We’ve also gone into each of the four functions that handle moving in a different direction and had them call the MoveTo function.
The MoveTo function
The first thing you may notice in this function are the lines that have a “//” in them. These are comments. Comments are ignored by the computer. They only exist for programmers to read, to know what the program is supposed to be doing. Everything after the double-slashes, until the end of the line, is ignored by the computer.
I used a lot more comments in this function than I normally would. That’s to make it easier for you to follow along with what is happening, and see how the code ties back to the pseudo-code we have above.
The second thing you may have noticed is that this function is long – over 300 lines long.
That’s really too long for a function.
When a function is that long, it’s difficult to keep track of what exactly it’s supposed to be doing. But we’re going to clean this up in the next lesson. Personally, I like to keep my functions around 10 to 40 lines long.
We’ll break this function into smaller functions in the next lesson.
What’s happening in the MoveTo() function?
On line 57, we have our first “if” statement.
In this case, we check if the new location has any items required to enter it.
The “!=” is how C# says “not equal to”. The exclamation point, when doing any sort of comparison in C#, means “not”. And “null” means nothing/empty.
So, if the ItemRequiredToEnter property of the location is not empty, we need to check if the player has the required item in their inventory. If it is empty, we don’t need to do anything – there is no required item, so the player can always move to the new location.
On line 72 we see if we found the required item in the player’s inventory. If we didn’t find an item with a matching ID, the “playerHasRequiredItem” variable will still be “false”.
Notice the exclamation point in front of playerHasRequiredItem.
Let’s assume the player does not have the required item in their inventory. The “playerHasRequiredItem” variable will have a value of “false”. Doing a “not” on a Boolean variable, reverses its value: “!true” equals “false”, and “!false” equals “true”.
Thinking “if not false” is not as clear as thinking “if true”. But they both mean the same thing.
On line 75, we display the message that the player is missing the item required to enter this location. This line has a new “+=” symbol.
“+=” means, take the value from the variable/property on the left, add the value on the right to it, and assign the results back into the variable/property on the left.
When you use “+=” with a string, it means, “add the string value on the right to the end of the existing string”. When you use it with a number, it means, “add the value on the right to the value on the left”.
Here, we take the text in the rtbMessages RichTextBox, and add our new message to the end of it. That way, the player can still see the old messages. If we used the “=” sign instead, it would replace the existing “Text” value with our new message.
We also have “Environment.NewLine”. This adds an “Enter” to the text, so the next thing we add to it will be displayed on the next line, instead of the end of the current line.
Line 76 has “return”. This means “exit out of this function”.
Since this function is a “void” function (see line 54), it doesn’t return a value. We can “return” here and not do the rest of the function. We want to do that in this case, because the player does not have the item required to enter the location. So, we don’t want to do the rest of the function, which would actually move them to the location.
On lines 84 through 87, we make the movement buttons visible, or not, based on whether or not the new location has a place to move to in each direction. We do this by checking if the property for the location is empty or not.
The “Visible” property of the buttons expects a Boolean value: true or false.
So, on line 84, if the LocationToNorth property is not empty, the value to the right of the equal sign will evaluate to “true”, and the button will be visible. If the LocationToNorth property is empty, this will evaluate to “false”, and the button will not be visible.
On line 100, we check if there is a quest at this location. If so, we need to do some more work.
Lines 106 through 117 are where we look through the player’s quest list, to see if they already have the quest at this location and if they already completed it.
Lines 128 through 163 looks at each item required to complete the quest, then checks each item in the player’s inventory, to see if they have it, and have enough of it, to complete the quest.
There are some “break” statements in this “foreach”. Those are used to stop looping through the items and exit the “foreach” loop. If we discover that the player doesn’t have one item, or enough of it, to complete the quest, we can stop checking for any other items.
Line 180 has a “-=”. The “+=” means, “add the value on the right to the variable/property on the left”. So, a “-=” means, “subtract the value on the right from the variable/property on the left”. You can only use this with numbers, and not strings, unlike the “+=”.
In this case, we are using it to remove items from the player’s inventory that they turn in to complete the quest.
On line 204, there is a “++”. When you have this after a variable or property, it means “add 1 to this variable or property”. There is also a “–“, for when you want to subtract 1 from a variable or property.
At lines 264 through 273, we create the new monster to fight, by making a new monster object, using the values from the standard monster in our World class.
Updating the DataGridViews in the UI
From lines 290 through 321 we update the DataGridView controls in the UI.
The player’s inventory will have changed if they completed a quest. The items needed to turn in were removed from their inventory. The reward item was added to their inventory. So, we need to update the UI with their current inventory.
Also, if they received a new quest, or completed an existing one, their quest list would change.
Updating the ComboBoxes in the UI
For the ComboBoxes in the UI, we create new lists to hold the specific datatype of items we want to show in the list (lines 324 and 353). Next, we go through all the items in the player’s inventory and add them to these lists, if they are the correct datatype (lines 326-335 and 355-364).
On lines 328 and 357, there is a new comparison: “is”. This is used to see if an object is a specific datatype.
Remember how we created the Weapon and HealingPotion sub-classes of the Item class? When you create a Weapon object, its datatype is both Weapon and Item. When you create a HealingPotion object, its datatype is both HealingPotion and Item.
If the lists are empty (weapons.Count or healingPotions.Count are equal to 0), we hide the ComboBox and “use” buttons, since there is no weapon or potion for the player to use.
If the lists have items in them, we “bind” the list to the comboboxes (lines 345-349 and 374-378). The “DisplayMember” determines what property will be displayed in the comboboxes. In this case, we want to display the value of the Name property. The “ValueMember” is the behind-the-scenes value we’ll use a little later, to know which item was selected.
NOTE: There are better ways to connect your list properties to DataGridViews and ComboBoxes. But we’re just concentrating on the basics in these tutorials.
NOTE: Hopefully you noticed something about the variable names. They are generally long and descriptive. That makes it easy to understand what values they are supposed to hold. By making your variable names descriptive, it will be easier to work with your program – especially when fixing bugs or making changes in the future.
There are a couple cases in the “foreach”s where I use short variables like “qci” and “ii”.
Since the foreach loop is only a few lines long, I sometimes use a shorter variable name. The variable has a very short life – it only exists within those few lines of the loop. You can display the whole loop on your screen, without scrolling. So, it’s easy to keep track of where the variable is populated and where it is used. If the loop was longer, I’d use a longer, more descriptive name.
Summary
Now you’ve seen how to plan out your logic in pseudo-code, and then create a function in the program to perform that logic.
You’ve seen how “if”s, “else”s, and “foreach”s are used in a function.
You’ve also seen what a huge function looks like, and you probably have an idea how difficult it would be to work with it. The next lesson will cover how to clean up that function and make it easier to understand.
We covered a lot in this lesson. If there was anything that wasn’t clear, please leave a comment below and I can update this lesson with some more details.
Source code for this lesson
Next lesson: Lesson 16.2 – Refactoring the player movement function
Previous lesson: Lesson 15.1 – Getting random numbers for the game
All lessons: Learn C# by Building a Simple RPG Index
Hi Scott,
I’m having a little trouble with this lesson. I pasted the required code into the Designer portion of the UI, but I’m getting the following error on a snippet of code. The code is as follows:
“this.dgvInventory.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellContentClick):”
MVS is telling me that I don’t have a definition for dataGridView1_CellContentClick or an extension method for dataGridView1_CellContentClick . How do I fix this?
Thank you!
That could have happened if you accidentally double-clicked on the datagrid control. Visual Studio would have created an “eventhandler” in SuperAdventure.Designer.cs, and an empty function in SuperAdventure.cs. The code you pasted in wouldn’t have the function, which is what the error is saying.
To fix it, you can edit SuperAdventure.Designer.cs. Find the line in the error message – the one with “this.dgvInventory.CellContentClick +=”, and delete it. That should fix the error.
You can read more about eventhandlers in Lesson 21.3, if you want a better understanding of them.
Please tell me if that does not fix the problem.
Hi Scott, terrific job you did right here!
Something I need to point out. I copied the whole content in your SuperAdventure.cs to mine (had to adjust some class names and some minor tweaks) but had an error on line 255:
“Error CS7036 There is no argument given that corresponds to the required formal parameter ‘isCompleted’ of ‘PlayerQuests.PlayerQuests(Quest, bool)’…”
I added a simple “, false” as a 2nd parameter, as it was asking for a bool value and the quest gotten should not be completed.
I didn’t see anyone else complaining about this so maybe I missed something on the PlayerQuests.cs file…
Any thoughts?
Thank you!
For the error you saw, check your PlayerQuest class against the one shown in Lesson 10.1. The constructor should only have one parameter – the “details” parameter, which is a Quest datatype.
If that isn’t the source of the problem, can you upload your SuperAdventure.cs, Player.cs, and PlayerQuest.cs files to https://gist.github.com, so I can look at them?
I really don’t know where to start with the errors i’m having…
First off: in SuperAdventure.cs
CS0103 The name ‘dgvInventory’ does not exist in the current context SuperAdventure
Secondly: in SuperAdventure.Designer.cs
CS1061 ‘SuperAdventure’ does not contain a definition for ‘label2_Click’ and no extension method ‘label2_Click’ accepting a first argument of type ‘SuperAdventure’ could be found (are you missing a using directive or an assembly reference?)
And the same error ^^^ for label5_Click , richTextBox_TextChanged and SuperAdventure_Load.
I’ve been following along the PDF version or the lessons.
What have i done wrong? ;W;
Hi Jacob,
For the error “‘dgvInventory’ does not exist”, edit SuperAdventure.cs in Design mode (the graphics screen). Left-click on the top-left datagrid one time and look at its properties in the lower-right section of Visual Studio. Make sure the “(Name)” value is “dgvInventory” – a capital “I”, as in “India”.
For the other errors, if you accidentally double-clicked on some of the controls (the labels, form, etc.), when you were in the Design screen for SuperAdventure, Visual Studio would have created functions and “eventhandlers”. Eventhandlers are what connects the UI controls to their functions in SuperAdventure.cs. If you pasted code into SuperAdventure.cs (in one of the lessons afterwards), it would not include the functions that Visual Studio created for those extra eventhandlers.
You can read more about how eventhandlers and the Designer file work in Lesson 21.3.
To fix the errors for the label2_Click, label5_Click, richTextBox_BoxChanged, and SuperAdventure_Load, you can modify SuperAdventure.Designer.cs
Find the lines with the four eventhandlers and delete them.
That should fix the errors. If it doesn’t please tell me, and we can look at it some more.
That fixed the problem.
I had created random event handlers which i deleted, allowing me to open the designer.
i had named dgvInventory “dbvInventory”, which happens way too often when i type.
A new error has popped up.
Application.Run(new SuperAdventure());
CS0118 ‘SuperAdventure’ is a namespace but is used like a type
Did you create the project with a different name? That might be one source for that problem. There is some information about how to fix renamed projects in Lesson 02.2B
If that is not the source of the error, can you upload your solution (including the folders under it, and the files in those folders) to GitHub or Dropbox, so I can look at it?
I can’t get _currentMonster in Super adventure to work for the life of me. It keeps saying “_currentMonster does not exist in the current context”.
Hi Matt,
Do your SuperAdventure.cs class have line 18 (from the code here), where the _currentMonster variable is declared?
If you do, can you please upload your solution (including the folders beneath it, and all the files in those folders) to GitHub or Dropbox, so I can look at it?
Hi Scott. Thanks for the tutorial, its been really fun and useful so far. I’ve now encountered a few errors after pasting the code into SuperAdventure.cs.
Most of it is fine but there is an error under the words “CurrentLocation” and “Weapon”.
The errors say
“‘Player’ does not contain a definition for ‘CurrentLocation’ and no extension method ‘CurrentLocation’ accepting a first argument of type ‘Player’ could be found (are you missing a using directive or an assembly reference?)”
and
“‘Weapon’ could not be found (are you missing a using directive or an assembly reference?)”
I’m guessing I’ve missed some lines maybe in some of the classes?
Do you know what I’d need to change to fix it?
Thanks for the help
Hi Stephen,
For the CurrentLocation, make sure you have the changes for the Player class that are in this code: https://gist.github.com/ScottLilly/208630cfcdded1cbfdc0#file-player-cs You especially want to check the CurrentLocation property on line 14. If that is there, make sure it is spelled exactly the same, including the “C” and “L” being upper-case.
For the Weapon error, do you have a “Weapon” class in your Engine project? That should have been created in some earlier lessons. The last lesson it was in was Lesson 08.2. If you have the class, make sure it is “public” on line 9 (see the full class code here).
If those do not fix the errors, can you upload your solution (including the folders underneath it, and all the files in those folders) to GitHub or Dropbox, so I can look at it?
Hi, thanks for the help. I must have accidentally missed that line “Public Location CurrentLocation { get; set; }” The weapon error was simply a matter of me having called it “weapons” instead of “weapon”.
The only problem I’m encountering now is an error message saying “‘SuperAdventure’ does not contain a definition for ‘Form1_Load’ and no extension method ‘Form1_Load’ accepting a first argument of type ‘SuperAdventure’ could be found (are you missing a using directive or an assembly reference?)”
I changed all instances I could find of the form from “form1 to “super adventure” earlier in development, however maybe I changed one that I shouldn’t have. I was hoping I could go onto the form properties and change the name back but it now won’t show and has a message reading:
“The designer cannot process unknown name ‘Form1_Load’ at line 295. The code within the method ‘InitializeComponent’ is generated by the designer and should not be manually modified. Please remove any changes and try opening the designer again.”
This means I can’t change the names of anything within the designer.
I have attempted to change form 1 to different versions of “superadventure” that I might have called it but the same error remains.
This form1_Load part is line 295 in the SuperAdventure.Designer.cs code. The full line is
this.Load += new System.EventHandler(this.Form1_Load);
which means the problem might not be directly related to the new code added in this lesson, however it was working fine last lesson.
Do you know what I need to change to fix it or should I go back to an older save and change the name back to form1?
Hi Stephen,
It sounds like you might have accidentally double-clicked on the SuperAdventure form, while in design mode. If you do that, Visual Studio will try to make a function and connect to it with an “eventhandler”. The error is saying that there is an eventhandler, but the class is missing the function it wants to connect to – which would have happened if you pasted in the code from a lesson (which would not have the extra function). For this program, we do not need to use the form’s “Load” event.
To fix the error, you can edit SuperAdventure.Designer.cs. If you delete line 295, or comment it out, that should fix the problem.
You can read more about eventhandlers in Lesson 21.3.
Please tell me if that does not fix the error, or if you have any other questions.
This has solved the problem and it seems to be running fine. Thank you so much for the help!
You’re welcome
Hey Scott.
Could you please explain line 165 step by step:
Monster standardMonster = World.MonsterByID(newLocation.MonsterLivingHere.ID);
I understand what the code is doing. I just don’t understand how 🙂
Thank you very much ! 🙂
Hi Tom,
When we created the Location object, we also created a Monster object, and set the Location’s MonsterLivingHere property to that Monster object. That Monster object has its own properties, like “ID”.
So, “newLocation.MonsterLivingHere.ID” looks at the location the player is moving to (the “newLocation” object), reads its MonsterLivingHere property (which is a Monster object, with its own properties) and reads the “ID” property from that monster object. That gives us the ID of the Monster that lives at that Location.
We call World.MonsterByID() and pass in the Monster’s ID value, to get the “standard” instance of that Monster object, which we store in the “standardMonster” variable – whose datatype is the “Monster” at the start of the line.
Does that make it clear?
Thank you very much ! Its more clear now.
I just try to understand how the program knows which Monster ID to compare before we pass it into the World.MonsterByID(). Where is it that we tell the program what to use as ID for the monster’s ID before we pass it into World.MonsterByID() ? I understand that Monster has an ID and that it exists in “newLocation”. But how does it know what ID it has when we haven’t passed anything into it yet? I hope my question makes sense 🙂
You’re welcome. For your question, we set the monster at the location inside the PopulateLocations() function of the World class.
Ahhh perfect, thank you !
Hi again Scott,
I moved on even though I haven’t worked through the previous errors. In the SuperAdventure.cs code in all instances where the World class was mentioned, it says that it doesn’t exist in that context. Also, the other major error is regarding when Monster was mentioned, it says it’s not available due to its protection level even though it is a public class. I’m a bit confused. If you could help it would be greatly appreciated.
Thanks,
-Taryn
Actually for this one never mind, I was able to fix this myself but there are some errors left, one regarding the RewardExperiencePoints, and two regarding SuperAdventure_Load.
The first one is:
-Severity ‘Monster’ does not contain a definition for ‘RewardExperiencePoints’ and no extension method ‘RewardExperiencePoints’ accepting a first argument of type ‘Monster’ could be found (are you missing a using directive or an assembly reference?) SuperAdventure
And the other two are:
-The designer cannot process unknown name ‘SuperAdventure_Load’ at line 291. The code within the method ‘InitializeComponent’ is generated by the designer and should not be manually modified. Please remove any changes and try opening the designer again.
-‘SuperAdventure’ does not contain a definition for ‘SuperAdventure_Load’ and no extension method ‘SuperAdventure_Load’ accepting a first argument of type ‘SuperAdventure’ could be found (are you missing a using directive or an assembly reference?)
Thanks for any help,
-Taryn
I have several comments from you, and it looks like the latest one is that the errors are fixed now. But, it looks like that is for your questions on lesson 11.1
If you still have the problems in this comment, you can do this:
The RewardExperiencePoints property was added to the Monster class in Lesson 06.1. Make sure you have it set to “public”.
SuperAdventure_Load was probably created if you accidentally double-clicked on the SuperAdventure form, when it was in “Design” mode. If you do that, Visual Studio creates an “eventhandler”, and a SuperAdventure_Load function in SuperAdventure.cs. Then, if you paste in the code from the lesson later, it does not have the SuperAdventure_Load function – which causes the error.
You can fix this by editing SuperAdventure.Designer.cs. Delete (or comment out) line 291 – the line with the SuperAdventure_Load.
You can learn more about eventhandlers in Lesson 21.3
Please tell me if that does not fix the errors in your program.
Hi Scott,
Thank you so much and I’m sorry my comments were so scattered yesterday, I just really wanted the program to work.
Regarding the Monster class adding public in front of it did fix the errors and the error with the RewardExperiencePoints was just a typo on my part, something, now I realise, should have been one of the first things I checked.
And the last error was fixed, I didn’t realise that piece of code was unnecessary. Now all the errors are fixed, again, thank you very much.
-Taryn
You’re welcome, Taryn
hey, scott I have a problem in line 289 in the SuperAdventure.Designer.cs
“his.Load += new System.EventHandler(this.SuperAdventure_Load)”
that’s said
“SuperAdventure’ does not contain a definition for ‘SuperAdventure_Load’ and no extension method ‘SuperAdventure_Load’ accepting a first argument of type ‘SuperAdventure’ could be found (are you missing a using directive or an assembly reference?)”
i don’t know what the problem is i have fowler you’re guides to everything
Hi Frederik,
SuperAdventure_Load was probably created if you accidentally double-clicked on the SuperAdventure form, when it was in “Design” mode. If you do that, Visual Studio creates an “eventhandler”, and a SuperAdventure_Load function in SuperAdventure.cs. Then, if you paste in the code from the lesson later, it does not have the SuperAdventure_Load function – which causes the error.
You can fix this by editing SuperAdventure.Designer.cs. Delete (or comment out) line 289 – the line with the SuperAdventure_Load.
You can learn more about eventhandlers in Lesson 21.3
Please tell me if that does not fix the errors in your program.
Hi Scott,
I really appreciate your time and effort here. It’s a great tutorial, one can learn a lot from.
I have few questions regarding MoveTo Method:
1. Is it OK if we just use
instead of
2. In your code,
why didn’t you create a new instance in LootTable (like we did in PopulateMonsters), e.g.:
But you just copied an existing lootItem of the standardMonster to the lootTable of currentMonster. Is there anything wrong if I instantiate it?
3. Could you explain “new[] { … }” in
Thank you very much in advance.
Hi Andy,
1. You need to use the “World.MonsterByID” code because we need a new instance of a Monster object each time. If you used newLocation.MonsterLivingHere, there would only ever be the one monster object. The player would always fight the exact same monster. After the player defeats the monster (get its hit points to 0), the monster will always have with 0 hit points, every time the player moves to that location. You can try replacing the code, playing the game, and see what happens. This is known as “maintaining state”. Each monster has a “state” (in this case, current hit points) that can change, independently of other monsters.
2. For the LootItems, we don’t need a new instance. Every “rusty sword” acts the same as every other rusty sword. We do not need to maintain state for loot items. So, we really only need one instance of “rusty sword”. We would only need individual “rusty sword” objects if they needed to be different – like if we wanted to customize them with gemstones (for enhancements), or if they could wear out with each attack.
3. The brackets signify an array – a type of collection of values. In this case, we want to add a new entry to the Inventory datagrid’s collection of rows. There are two columns in the row: the item’s name, and the quantity of items. The value of “inventoryItem.Details.Name” will be displayed in the first column, and the value for “inventoryItem.Quantty.ToString()” will be displayed in the second column. In a future lesson, we change this to use “databinding”, to get the values for the datagrid’s rows differently.
Please let me know if that didn’t answer all your questions.
Hi Scott,
Thank you for your replies.
1. INHO standardMonster is something like template based on which we create currentMonster. So in both cases below, standardMonster refers to the same object we created in World class. I tested it and it worked fine. Please correct me if I am wrong:
Another question:
I tried to run following “meaningless” code:
And I got following exception:
An unhandled exception of type ‘System.InvalidOperationException’ occurred in mscorlib.dll
Additional information: Collection was modified; enumeration operation may not execute.
I know it doesn’t make sense and that they both standardMonster and _currentMonster refer to the same object in memory, but I only want to understand the exception.
Thank you.
Hi Andy,
I finally found a version of the program from this version (I only have the latest version on my computer, since I originally wrote this three years ago, on a different computer).
You are correct for this version of the code, because we instantiate a new Monster object on the line after we create the standardMonster variable. In a future lesson, we combine those two lines into one. So, that one line needs to instantiate the new Monster object. It does that by using a cloning function for the Monster class.
That error is happening because you are trying to modify the number of items in a List property, while you are also looping through that List property. Let’s say there are two item in the monster’s LootTable list property. So, you are trying to loop through that list of two items. But, the first thing you do is add a new item to that list. Now it has three items in the list – while the “foreach” loop started with it only having two items. To prevent this type of problem (if you ever need to modify the number of items in a list, while looping through it), you need to add an extra step. You would add items to a temporary list variable, then (in a separate loop), add the objects from the new list variable into the first list variable/property.
Hello Scott, i ran into two small issues, one i solved
First was that i had error with line (SuperAdventure.Designer.cs)
this.cboPotions.SelectedIndexChanged += new System.EventHandler(this.cboPotions_SelectedIndexChanged);
I wasn’t sure why. cboWeapons was working fine, i deleted the line to be able to go into designer without getting error causing collapse of design. I double clicked on the cboPotion button (thinking i need to initialize this button mabye) and it added line to superadvenutre.cs
private void cboPotions_SelectedIndexChanged(object sender, EventArgs e)
{
}
SUDDENLY it works fine, no issue…strangely, i did the same for weapon cbo box, but it had no problem in a first place…iam not really sure what happend there and why suddenly SuperAvdentuure.Designer.cs had problem after adding code to SuperAdventure.cs and Player.cs.
Also it now got a problem with line
this.Load += new System.EventHandler(this.SuperAdventure_Load);
again designer, i suppose it has something to do with Load function which we did not implement nor i have button for it. Strange things are happening 🙂
If you accidentally double-click on a user control, when you are in a form’s designer screen, Visual Studio will create an eventhandler (the line in the SuperAdventure.Designer.cs) and a default function (in SuperAdventure.cs). If you paste in the lesson code to SuperAdventure.cs, you will get an error – because the eventhandler is trying to find a function that does not exist in the lesson code. The easiest way to fix this is to delete the line in SuperAdventure.Designer.cs.
You can read more about eventhandlers, and how they work, in Lesson 21.3.
Please tell me if this does not fix the errors.
Thank you, it did! I simply didn’t want to delete it because it works after, but to know why it works 🙂
You’re welcome!
Hello Scott, i love this tutorial.
I am an absolut beginner, and ran into a problem.
I got an error in Program.cs, which i don’t recall we’ve worked with so far?
Here is the code, though i imagine you have the same, except i named the game “game”, instead of “SuperAdventure”.
The error says the problem is with “Application.Run(new Game());”.
It says ” ‘Game’ is a namespace but is used like a type”
What is the problem?
Hello Emil,
Since you have a different name for the project, you will need to change the “namespace”. There are instructions to do that here: https://www.scottlilly.com/learn-c-by-building-a-simple-rpg-index/lesson-02-2b-fixing-the-namespace/
Please tell me if that does not fix the problem.
Hey Scott,
I’m having an issue where clicking the north button isn’t moving my location at all. I would really appreciate some help lol.
It sounds like the button does not have its “eventhandler” – the line of code that says what function to run when the button is clicked.
Look in SuperAdventure.Designer.cs, and see if it has this line:
this.btnNorth.Click += new System.EventHandler(this.btnNorth_Click);
If that line does not exist, add it near the other “btnNorth” lines.
There is more information on how to use eventhandlers in Lesson 21.3.
Please tell me if that does not fix the problem.
Awesome, thanks! It began working perfectly once I added that in. Also I’m wondering if there’s anything that would have caused that to happen off the top of your head?
You’re welcome! In one of the earlier lessons (when the buttons were added) there’s a step to double-click on each button in the designer screen. When you do that, Visual Studio creates the eventhandler and an empty function. If you accidentally missed one of the buttons, or only single-clicked on it, then the eventhandler wouldn’t have been created. That’s probably why the eventhandler didn’t exist for that button.
Hi Scott, I have a problem in a number of lines within the SuperAdventure.CS anywhere that references rtbMessages.Text is shown as an error with the following “the name ‘rtbMessages’ does not exist in the current context”
I’m not too sure how to resolve this.
Thanks,
Andrew
Hi Andrew,
If you open the SuperAdventure form in Visual Studio, and left-click once on the RichTextBox, you can see its properties in the lower-right corner of Visual Studio. Scroll to the Design section, and verify that the “Name” value is “rtbMessages”. If it has a different name, change it there.
If that is not the problem, or that doesn’t fix the error, please let me know.
Hi Scott, I learn a lot from you thank you for that!
Could you please help me with this:
Error CS1061 ‘Quest’ does not contain a definition for ‘QuestCompletionItems’ and no extension method ‘QuestCompletionItems’ accepting a first argument of type ‘Quest’ could be found (are you missing a using directive or an assembly reference?)
Line: 128, 173, 241
And:
Error CS1061 ‘Monster’ does not contain a definition for ‘LootTable’ and no extension method ‘LootTable’ accepting a first argument of type ‘Monster’ could be found (are you missing a using directive or an assembly reference?)
Line: 270, 272
All in SuperAdventure.cs !
Hi Malachi,
In the Quest class, do you have the property QuestCompletionItems, and is it “public”? The same for the LootTable property in the Monster class.
If those properties are missing, double-check steps 4 and 5 in Lesson 10.1, to see exactly what needs to be in those classes.
Please tell me if that does not fix the problem.
Yes I somehow missed those Lines!
Thank you for your help!
Hi Scott !
I know that this a silly question, but what this lane stands for ?
private void MoveTo(Location newLocation)
I guess it is a method where we put location and newLcoation without getting anything, but why there is a newLocation, because we haven’t initialazed that value anywhere before that… As i found this was the first time we we used it and also in the next usages we stil haven’t created that valuable..
That is the line to start the MoveTo() function. We will call that function when we want to move a player to a new location.
The “Location newLocation” is the parameter list – the value we are going to pass to the function. “Location” is the datatype = what type of object we are sending into the function. “newLocation” is the parameter name for the object we pass in.
We call the MoveTo function on lines 36, 41, 46, and 51 of SuperAdventure.cs. When we call the function, we pass in a Location object (for example, “_player.CurrentLocation.LocationToNorth”). Inside the MoveTo function, when we use the “newLocation” parameter/variable, it is the value that was sent in (in this case, “_player.CurrentLocation.LocationToNorth”).
Does that make sense?
yea, i refreshed some memories, thank you
Thanks for the great tutorial! It helps me a lot.
You’re welcome
Hi Scott
First off thank you so much for creating this awesome tutorial! These lessons rock! I do have a question for you. The code in my SuperAdventure.cs file seems to exactly match yours but I can’t seem to get rid of the error I’m getting. In lines 309 to 321 I cant get the red lines under dvgQuests to go away. The errors all say “the name dvgQuests does not exist in the current context. Any help would be greatly appreciated.
You’re welcome, Drew. Open your SuperAdventure form in design mode, and check the exact spelling and upper/lower-case name of the quest datagrid control. It needs to exactly match what is in the code in SuperAdventure.cs. It should be “dgvQuests”.
Let me know if that does not fix the errors.
Hi!
I’m a little confused. At the start of the tutorial you mentioned separating the GUI from the game logic, which sounds perfectly reasonable. Yet here we are, adding ~300 lines of game logic code to the GUI itself without so much as blinking an eye. Shouldn’t we rather have some sort of controller class as part of the Engine, which keeps track of everything, checks the conditions and decides what’s going to happen?
Seems like I was being impatient. The issue is addressed in the next section of this tutorial. 😉
No problem. 🙂 This is one thing I wish I could do over – so we don’t need to do the refactoring. But, I’d need to rewrite almost all the lessons from the beginning. 🙁