Lesson 12.1: Making the GameItem class more flexible

The next feature I want to add to the game is the ability to craft consumable items – food and potions the player can use to to heal themselves. But, this will require several changes to the program.


We need to make GameItems that can be eaten and give an affect (healing, in this case). We also need to add the ability for the player to craft items. This means adding recipes.

We could make this change by create new sub-classes of the GameItem class. But, we don’t want to need to create new GameItem Sub-classes for each new type of item.


So, we’re going to use two design patterns: Composition Over Inheritance and the Command Pattern. If you aren’t familiar with design patterns, they’re a set of common techniques to follow when trying to write a program.


Because there are many changes to make, I’ll split these changes into multiple lessons. In each lesson, we’ll make a small change and ensure we can still play the game.

This is safer than making hundreds of changes at the same time. If we did that, and something didn’t work, it would be difficult to find out which change broke the program. By making small changes, there won’t be many places to look, if we break something.


In this lesson, we’ll eliminate the Weapon class, and move its properties into the GameItem class.



Lesson Steps

Step 1: Modify Engine\Models\GameItem.cs

We need a way to identify what type of item a GameItem is (Weapon, Food, Armor, etc.). So, we’ll add a Category property (line 11). Its datatype is the new ItemCategory enum (lines 5-9).

An “enum” is like a list of constants. One enum that already exists in the .NET Framework is “DayOfWeek”. It lets you write code like this:

“if(DateTime.Now.DayOfWeek = DayOfWeek.Monday)”

So, you aren’t forced to remember which integer value represents Monday. When you compile your program, the enums are converted to integer values. But, when you write your code, you get to use the easier-to-understand value.


On lines 16 and 17, we have the MinimumDamage and MaximumDamage properties that used to be in the Weapon class.


Finally, we changed the constructor and Clone functions to accept and use the new properties.





Step 2: Delete Engine\Models\Weapon.cs

Because we have the MinimumDamage and MaximumDamage properties in GameItem, we don’t need the Weapon sub-class any more.

When you delete this class, your project will have several errors – where the program was using the Weapon class. We need to fix those by having the code look at GameItem.Category to determine if a GameItem is a weapon.



Step 3: Modify Engine\Models\LivingEntity.cs

The Weapons property (lines 72-73) used to look in the player’s Inventory property and return a list of everything that “is Weapon” (whose datatype is Weapon).

Now, we’ll change it to check if the GameItem’s Category equals ItemCategory.Weapon.





Step 4: Modify Engine\ViewModels\GameSession.cs

The CurrentWeapon property (line 101) had a datatype of “Weapon”. Change it to “GameItem”.





Step 5: Modify Engine\Factories\ItemFactory.cs

Because we changed the GameItem constructor, and deleted the Weapon class, we need to change how the factory constructs the standard game items.

Before, the CreateGameFunction needed to check if the item was a Weapon, so it could use the Clone function from Weapon (instead of the one from GameItem). Now, it can always use the one in GameItem. We can reduce this function to one line.

To construct the standard GameItem objects, we could have just changed lines 13-21, to pass in the new values needed in the GameItem constructor. However, I created two new “builder” functions: BuildMiscellaneousItem and BuildWeapon. We’re going to need them soon, and they make the code a little cleaner (in my opinion).

There’s a principle in Agile programming known as YAGNI – You Aren’t Gonna Need It. You shouldn’t write code for things you think you’re going to add in the future. You may not need it. So, you would have wasted your time writing that code.

However, I’m 99.9% sure we’ll need this. I started writing all the changes for food items (using Composition Over Inheritance and the Command Pattern), and I quickly needed these functions.





Step 6: Run the unit tests and test the game

Even though we only have two unit tests right now, we’ll run them to see if we broke any of the code they cover.

Since we don’t have many unit tests, it’s good to also play the game and see if the changes broke anything.


Return to main page

Leave a Reply

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