Lesson 07.4: Monster Combat

Now that we can send messages from the ViewModel to the View, we can add the code for the player to fight the monsters.

 

 

LESSON STEPS

 

Step 1: Edit Engine\Models\Player.cs

We want to give the player the ability to select the weapon to fight with. So, we’re going to display a combobox (dropdown box) on the screen, with all their weapons.

To do this, we’re going to create a new property in the Player class. This is the Weapons property (lines 80 and 81).

Because this property is a subset of the Inventory property, we won’t use a getter and setter. It will just be a LINQ query of all objects in the Inventory property whose datatype is Weapon.

We need to add the ToList() at the end of the LINQ Where() because LINQ queries are not actually run until they have to do something with the results. This is called “deferred execution”.

 

Because this property doesn’t have a “set”, we need to manually raise the property changed event for it, whenever its value changes. We’ll do that in the AddItemToInventory (lines 93 through 98).

We could add GameItem objects directly to the Inventory property. However, using this function gives us some extra capabilities.

For now, the function will only add the item to the player’s inventory and raise a property changed event for the Weapons property. In the future, we will could use this to limit the player’s inventory – if we wanted to add weight to the GameItems, and limit the amount of weight a player can carry.

 

Player.cs

 

 

Step 2: Edit Engine\Factories\ItemFactory.cs

Currently, the ItemFactory function CreateGameItem() returns an object with a GameItem datatype. That’s the base datatype for all game objects. In order to know which items in the player’s inventory are weapons, we need to make a change to CreateGameItem().

When CreateGameItem creates a new object, it uses the Clone function on the “standardItem” object. However, since “standardObject” is declared as a GameItem, this will use the Clone function in the GameItem class.

This returns a GameItem object, that only has its ItemTypeID, Name, and Price properties set (the only parameters used in the Clone function). When we create a Weapon object, we need to use the Clone function from the Weapon class.

To do this, we’ll add the code on lines 31 through 34.

If the standardItem’s datatype is Weapon, we will cast it as a Weapon object (the “(standardItem as Weapon)” part of line 33), and then call its Clone function. This will use the Clone function from the Weapon class.

 

ItemFactory.cs

 

 

Step 3: Edit Engine\Models\Monster.cs and Engine\Factories\MonsterFactory.cs

Now that the player can have a weapon, it’s only fair to let the monsters do some damage.

In the future, we can add the ability for monsters to use weapons. But, for now, because all our monsters are animals, they will only do damage by biting.

We’ll add two new properties to the Monster class: MinimumDamage and MaximumDamage (both integer properties) – on lines 21 and 22.

To populate these properties, we’ll add two new parameters to the Monster constructor (line 31), and set the properties with the passed-in parameter values (lines 38 and 39).

Now we need to pass values for these parameters when we instantiate a Monster object in the MonsterFactory.

Change lines 14 and 23, to pass in 1 as the minimumDamage and 2 as the maximumDamage. Because spiders are much more powerful, I changed line 32 to pass in 1 as the minimumDamage, and 4 as the maximumDamage.

 

MonsterFactory.cs

Monster.cs

 

 

Step 4: Edit WPFUI\MainWindow.xaml and MainWindow.xaml.cs

We will display the combat controls in MainWindow.xaml, using the code in lines 212 through 237.

On line 228 is a new ComboBox control. This displays a selectable dropdown box. We’ll populate it with the player’s Weapons (the ItemSource), and bind the GameSession’s “CurrentWeapon” property to the SelectedItem of the ComboBox. This way, if the player changes the item in the ComboBox, it will update the CurrentWeapon property – and we will use the correct weapon during combat.

On 234-236 is the button that will call the combat function “OnClick_AttackMonster”.

 

In MainWindow.xaml.cs, the new function to attack the monster (OnClick_AttackMonster) is on lines 46 through 49. It will call the AttackCurrentMonster function we will create next in the GameSession class.

 

MainWindow.xaml

 

 

MainWindow.xaml.cs

 

 

Step 5: Edit Engine\ViewModels\GameSession.cs

Now, everything is ready to add the logic for the player to fight monsters.

The first thing we need to do is add the CurrentWeapon property on line 57. We don’t have a backing variable for this property because it will only ever be changed from the UI.

If we ever want to change the value from the ViewModel, or Model, we would need to raise a property changed event, to let the UI know. But, we aren’t going to do that now.

 

Next, we need to do is give the player a weapon. Inside the GameSession constructor, on lines 107 through 110, we check the player’s Weapons property. If there are not any objects in that property, we will get a Pointy Stick (item 1001) from the ItemFactory, and give it to the player.

 

Then, we will finally write the combat function “AttackCurrentMonster” (lines 165 through 234). This function is longer than I like functions to be. But, we will do our refactoring (clean-up) after we get the code working.

On lines 167-171, we check if there is no weapon selected. If there isn’t, we use the RaiseMessage function to display the message in the RichTextBox in the UI. Then, we return from the function on line 170.

This is sometimes called “early exit”. If there is something that will prevent the rest of the function from working, we return from the function before we try to run the rest of the function. This is a common pattern for handling validation that you have all the values you need.

On line 174, we get the damage to do to the monster. And, on lines 176 through 184, we raise a message about how much damage the player did to the monster (or if they didn’t do any damage). On line 182, we also subtract the damage from the monster’s hit points.

If you haven’t seen it before, “-=” is another way to say, “CurrentMonster.HitPoints = CurrentMonster.HitPoints – damage”. This line takes the value of the HitPoints property, subtracts the damage, and assigns the results back into the HitPoints property. This is like the “+=” we used in the Location class, to add together the ChanceOfEncountering for all the monsters in the MonstersHere list.

On lines 187 through 207, we handle the player defeating the monster.

If the monster’s hit points are at 0 (or less), we give the player the rewards for defeating the monster, and call RaiseMessage to show the rewards on the UI.

On line 206, we call GetMonsterAtLocation(), so the player has a new monster to fight.

The “else” condition on line 208 is to handle when the monster is still alive. Now, it’s time for the monster to attack.

The logic for the monster attack is like the logic for the player attack. We get a random amount of damage, subtract hit points (if the damage was not zero), and display some messages.

However, if the monster defeats the player, the monster is not rewarded. Instead, on lines 223 through 231, we display a message that the player was killed, we move the player back to their home, and we completely heal the player – so they can return to battle.

In the future, we can make this more complex – determining who attacks first, allowing damage over time (from poison or spells), etc. However, this is what we will start with.

 

GameSession.cs

 

 

Step 6: Test the game

Everything should be ready for the player to fight monsters. Run the program, move to a location with a monster, and see if you can defeat it – and collect your gold and loot!

 

Game combat screen - with player and mosnter damage

 

Return to main page

 

8 thoughts on “Lesson 07.4: Monster Combat

  1. Hi Scott,
    I’m having trouble in this step, I can’t set this line in GameSession.cs:

    if (CurrentPlayer.Weapons.Any())
    {
    CurrentPlayer.AddItemToInventory(ItemFactory.CreateGameItem(1001));
    }

    to
    if(!CurrentPlayer.Weapons.Any())

    I’m getting an error of “Value cannot be null”. The weapon comboBox doesn’t populate any weapons for the player.

    1. Hi Rocio,

      I found the source of the problem. The GameItem.cs constructor has this code:

      itemTypeID = ItemTypeID;
      name = Name;
      price = Price;

      Those lines are setting the values of the parameters to the values of the GameItem properties. It needs to be the opposite – the properties need to be set to the parameter values. like this:

      ItemTypeID = itemTypeID;
      Name = name;
      Price = price;

      Please tell m if that does not fix the error, or if you have any questions.

Leave a Reply

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