Lesson Objectives
At the end of this lesson, you will know…
- Nothing new. We’re just finishing the program, using the same things you learned in the previous lessons.
Now we’ll write the functions the player will use when fighting monsters.
We have two things they can do (besides click on one of the direction buttons, to run away from the battle): use a weapon on the monster or use a healing potion on themselves – if they have one in their inventory.
Here is the pseudo-code for the two functions:
Use a weapon function
- Get the currently selected weapon from the cboWeapons ComboBox
- Determine the amount of damage the player does to the monster
- Apply the damage to the monster’s CurrentHitPoints
- Display message
- If the monster is dead (zero hit points remaining)
- Display a victory message
- Give player experience points for killing the monster
- Display message
- Give player gold for killing the monster
- Display message
- Get loot items from the monster
- Display message for each loot item
- Add item to player’s inventory
- Refresh player data on UI
- Gold and ExperiencePoints
- Inventory list and ComboBoxes
- “Move” player to current location
- This will heal the player and create a new monster
- If the monster is still alive
- Determine the amount of damage the monster does to the player
- Display message
- Subtract damage from player’s CurrentHitPoints
- Refresh player data in UI
- If player is dead (zero hit points remaining)
- Display message
- Move player to “Home” location
Use a potion function
- Get currently selected potion from cboPotions ComboBox
- Add healing amount to player’s CurrentHitPoints
- CurrentHitPoints cannot exceed player’s MaximumHitPoints
- Remove the potion from the player’s inventory
- Display message
- Monster gets their turn to attack
- Determine the amount of damage the monster does to the player
- Display message
- Subtract damage from player’s CurrentHitPoints
- Refresh player data in UI
- If player is dead (zero hit points remaining)
- Display message
- Move player to “Home” location
- Refresh player data in UI
These are much simpler functions than the MoveTo() function.
We’ll be able to use some of the smaller functions we created during the refactoring lesson – for example, the AddItemToInventory() function, from the Player class, if the player defeats the monster and receives loot items.
Adding functions for monster battles
Step 1: Start Visual Studio Express 2013 for Desktop, and open the solution.
Step 2: Right-click on the SuperAdventure.cs form in the SuperAdventure project, to start working with the code for the UI.
To add the ability to use weapons and potions in combat, you can add in the code below, or replace the code for SuperAdventure.cs with the code here: https://gist.github.com/ScottLilly/b20787650f2ab2a78362
For the btnUseWeapon_Click function, add this:
private void btnUseWeapon_Click(object sender, EventArgs e)
{
// Get the currently selected weapon from the cboWeapons ComboBox
Weapon currentWeapon = (Weapon)cboWeapons.SelectedItem;
// Determine the amount of damage to do to the monster
int damageToMonster = RandomNumberGenerator.NumberBetween(currentWeapon.MinimumDamage, currentWeapon.MaximumDamage);
// Apply the damage to the monster's CurrentHitPoints
_currentMonster.CurrentHitPoints -= damageToMonster;
// Display message
rtbMessages.Text += "You hit the " + _currentMonster.Name + " for " + damageToMonster.ToString() + " points." + Environment.NewLine;
// Check if the monster is dead
if(_currentMonster.CurrentHitPoints <= 0)
{
// Monster is dead
rtbMessages.Text += Environment.NewLine;
rtbMessages.Text += "You defeated the " + _currentMonster.Name + Environment.NewLine;
// Give player experience points for killing the monster
_player.ExperiencePoints += _currentMonster.RewardExperiencePoints;
rtbMessages.Text += "You receive " + _currentMonster.RewardExperiencePoints.ToString() + " experience points" + Environment.NewLine;
// Give player gold for killing the monster
_player.Gold += _currentMonster.RewardGold;
rtbMessages.Text += "You receive " + _currentMonster.RewardGold.ToString() + " gold" + Environment.NewLine;
// Get random loot items from the monster
List<InventoryItem> lootedItems = new List<InventoryItem>();
// Add items to the lootedItems list, comparing a random number to the drop percentage
foreach(LootItem lootItem in _currentMonster.LootTable)
{
if(RandomNumberGenerator.NumberBetween(1, 100) <= lootItem.DropPercentage)
{
lootedItems.Add(new InventoryItem(lootItem.Details, 1));
}
}
// If no items were randomly selected, then add the default loot item(s).
if(lootedItems.Count == 0)
{
foreach(LootItem lootItem in _currentMonster.LootTable)
{
if(lootItem.IsDefaultItem)
{
lootedItems.Add(new InventoryItem(lootItem.Details, 1));
}
}
}
// Add the looted items to the player's inventory
foreach(InventoryItem inventoryItem in lootedItems)
{
_player.AddItemToInventory(inventoryItem.Details);
if(inventoryItem.Quantity == 1)
{
rtbMessages.Text += "You loot " + inventoryItem.Quantity.ToString() + " " + inventoryItem.Details.Name +Environment.NewLine;
}
else
{
rtbMessages.Text += "You loot " + inventoryItem.Quantity.ToString() + " " + inventoryItem.Details.NamePlural + Environment.NewLine;
}
}
// Refresh player information and inventory controls
lblHitPoints.Text = _player.CurrentHitPoints.ToString();
lblGold.Text = _player.Gold.ToString();
lblExperience.Text = _player.ExperiencePoints.ToString();
lblLevel.Text = _player.Level.ToString();
UpdateInventoryListInUI();
UpdateWeaponListInUI();
UpdatePotionListInUI();
// Add a blank line to the messages box, just for appearance.
rtbMessages.Text += Environment.NewLine;
// Move player to current location (to heal player and create a new monster to fight)
MoveTo(_player.CurrentLocation);
}
else
{
// Monster is still alive
// Determine the amount of damage the monster does to the player
int damageToPlayer = RandomNumberGenerator.NumberBetween(0, _currentMonster.MaximumDamage);
// Display message
rtbMessages.Text += "The " + _currentMonster.Name + " did " + damageToPlayer.ToString() + " points of damage." + Environment.NewLine;
// Subtract damage from player
_player.CurrentHitPoints -= damageToPlayer;
// Refresh player data in UI
lblHitPoints.Text = _player.CurrentHitPoints.ToString();
if(_player.CurrentHitPoints <= 0)
{
// Display message
rtbMessages.Text += "The " + _currentMonster.Name + " killed you." + Environment.NewLine;
// Move player to "Home"
MoveTo(World.LocationByID(World.LOCATION_ID_HOME));
}
}
}
Then, for the btnUsePotion_Click function, add this:
private void btnUsePotion_Click(object sender, EventArgs e)
{
// Get the currently selected potion from the combobox
HealingPotion potion = (HealingPotion)cboPotions.SelectedItem;
// Add healing amount to the player's current hit points
_player.CurrentHitPoints = (_player.CurrentHitPoints + potion.AmountToHeal);
// CurrentHitPoints cannot exceed player's MaximumHitPoints
if(_player.CurrentHitPoints > _player.MaximumHitPoints)
{
_player.CurrentHitPoints = _player.MaximumHitPoints;
}
// Remove the potion from the player's inventory
foreach(InventoryItem ii in _player.Inventory)
{
if(ii.Details.ID == potion.ID)
{
ii.Quantity--;
break;
}
}
// Display message
rtbMessages.Text += "You drink a " + potion.Name + Environment.NewLine;
// Monster gets their turn to attack
// Determine the amount of damage the monster does to the player
int damageToPlayer = RandomNumberGenerator.NumberBetween(0, _currentMonster.MaximumDamage);
// Display message
rtbMessages.Text += "The " + _currentMonster.Name + " did " + damageToPlayer.ToString() + " points of damage." + Environment.NewLine;
// Subtract damage from player
_player.CurrentHitPoints -= damageToPlayer;
if(_player.CurrentHitPoints <= 0)
{
// Display message
rtbMessages.Text += "The " + _currentMonster.Name + " killed you." + Environment.NewLine;
// Move player to "Home"
MoveTo(World.LocationByID(World.LOCATION_ID_HOME));
}
// Refresh player data in UI
lblHitPoints.Text = _player.CurrentHitPoints.ToString();
UpdateInventoryListInUI();
UpdatePotionListInUI();
}
There isn’t really anything new in these two functions. Just more “if”s and “foreach”s to handle the player’s actions in battle.
Summary
Now you have a working game. The player can move around in the world, get quests, battle monsters, receive loot, and complete quests.
These new functions could use some refactoring, since they are long and do several things. I’ll leave that to you to figure out what refactoring you’d do.
Source code for this lesson
Next lesson: Lesson 17.1 – Running the game on another computer
Previous lesson: Lesson 16.2 – Refactoring the player movement function
All lessons: Learn C# by Building a Simple RPG Index
Hello Scott,
I love the tutorial so far and am looking forward to completing it. Although, I’ve run into an error when I try to build my project. I’m getting the error code CS0246; telling me that the ‘Engine,’ ‘Monster,’ ‘Location,’ and ‘Player’ namespaces cannot be found.
Seeing as ‘Engine’ is the only actual namespace, I assume it’s not being referenced somewhere, but everywhere I look seems to not have any issues. Hopefully you might be able to give me some insight on the problem.
Hi Ezekiel,
I’m not sure if the game worked before the changes from this lesson, but these are the things I’d check:
1. Make sure the UI project has a reference to the Engine project (from lesson 02.2) The newer version of Visual Studio looks little different when adding project references. It’s still in Solution Explorer, but under the project’s “Dependencies” -> “Projects”.
2. Make sure the namespace lines match. These lines are towards the top of the Monster, Location, and Player classes, and would need to be have a “using Engine;” line in classes that use those classes.
3. Make sure the Monster, Location, and Player classes all have “public” near the top, where it says “public Monster” (and the same for the other classes).
If that doesn’t solve the error, can you please upload your solution (including the directories under it, and all the files in those directories) to GitHub, Dropbox, or some other file-sharing location so I can look at it?
If you haven’t used GitHub before, here is a video on how to upload your solution to GitHub and share it with me. https://youtu.be/0si9ElYQv8I
Nevermind. I just cleaned the solution and found that one curly bracket was missing in the World class smh. I love coding.
Great to hear. Unmatched curly braces and missing semi-colons are always “fun” to track down. 🙂
Hello Scott,
Really enjoying the tutorial. I’m running into a problem. When I run the program and click on the use weapon button, nothing happens. I hoping you might have an idea on what could be wrong. Thanks!
Hi Louis,
My first guess is that the button’s “eventhandler” is not connected to the function that does the “use weapon” code. Lesson 21.3 has information on eventhandlers, and how to check them (or manually create them).
If that doesn’t fix things, can you please upload your solution (including the directories under it, and all the files in those directories) to GitHub, Dropbox, or some other file-sharing location so I can look at it?
If you haven’t used GitHub before, here is a video on how to upload your solution to GitHub and share it with me. https://youtu.be/0si9ElYQv8I
I have the same issue. Did you ever figure it out?
Hi Blake,
If reading about the eventhandlers (in lesson 21.3) doesn’t help, can you please upload your solution (including the directories under it, and all the files in those directories) to GitHub, Dropbox, or some other file-sharing location so I can look at it?
If you haven’t used GitHub before, here is a video on how to upload your solution to GitHub and share it with me. https://youtu.be/0si9ElYQv8I
I have the same issue where can i upload my project file? So, hopefully you can help me.
Hello Ayhan,
You can upload your solution (including the directories under it, and all the files in those directories) to GitHub, Dropbox. If you have another file sharing location (like OneDrive), you can also upload it there and reply to this message with a link to it.
If you haven’t used GitHub before, here is a video on how to upload your solution to GitHub and share it with me. https://youtu.be/0si9ElYQv8I
Here’s the link LINK REMOVED FOR PRIVACY
Ayhan,
The eventhandlers for Click event of the East and West buttons is not connected. So, when you click those buttons, the program does not know it needs to run the btnEast_Click and btnWest_Click functions.
To fix this, edit the SuperAdventure screen. Left-click on the East button once and look at the properties section in Visual Studio. If you click the lightning bolt, that will show you the events. Find the Click even and set the value to “btnEast_Click”. Do the same for the West button, connecting it to btnWest_Click.
Please let me know if that does not work.