Lesson 10.2: Grouping GameItems in Inventories

Inventories currently hold one object for each GameItem.

We need to do this because we will eventually have effects that are unique for each GameItem – such as enchanting a weapon. However, there are some GameItems that will never have special effects – like snakeskins.

We’re going to make changes to allow us to distinguish between unique items and non-unique items. This will let us have fewer lines in our Inventory datagrids.





Lesson Steps

Step 1: Modify Engine\Models\GameItem.cs

Add the Boolean “IsUnique” property to the GameItem class.

Add a parameter to the constructor, to let us pass in a Boolean value for the IsUnique property. The “= false” is the default value for this parameter. If we call the constructor without passing in this parameter, it will use a value of “false”.

You can only setup default values for parameters at the end of the parameter list. If you had a default value for a parameter in the middle of the parameter list, the compiler might not know which parameter is missing.


In the Clone function, on line 20, we want to pass the current object’s IsUnique value into its clone.





Step 2: Modify Engine\Models\Weapon.cs

In the Weapon constructor, where we call the base class constructor (GameItem), pass “true” for the “isUnique” parameter.

This is because we want all weapons to be unique. Weapons will eventually be able to have enchantments, and maybe wear out from use. So, two “pointy sticks” could be different from each other.





Step 3: Create the new class Engine\Models\GroupedInventoryItem.cs

This class holds a GameItem object and a Quantity. It’s like ItemQuantity, except it holds a complete GameItem – instead of only the GameItem’s ID. This is because we need some of the GameItem’s properties (Name and Price), to display in the UI.

GroupedInventoryItem needs to inherit from BaseNotificationClass, so we can raise a property changed event when the Quantity increases or decreases.





Step 4: Modify Engine\Models\LivingEntity.cs

On line 56, add the new property GroupedInventory, whose datatype is ObservableCollection<GroupedInventoryItem>.

Initialize that property on line 64 of the constructor.


Change the AddItemToInventory function on line 67. After adding the item to the Inventory, we need to either:

1) add it to GroupedInventory (if it is unique, or not already in GroupedInventory), or

2) increment its Quantity (if it’s already in GroupedInventory).

On line 79, notice that we add new non-unique items with a quantity of zero. That’s because we’re going to increment the quantity by one on line 82.


Change the RemoveItemFromInventory function on line 88.

On line 92, we get the first GroupedInventoryItem from the GroupedInventory property. If that value is not null (it should never be null, but it’s good to check), we’ll either:

1) completely remove the GroupedInventoryItem from GroupedInventory if its quantity is one (which it will always be for unique items, and could possibly be for non-unique items), or

2) decrease its quantity by one on line 103


We might make more changes in the future, so there is only one property for the inventory. But, this is how we will make the changes now.





Step 5: Modify WPFUI\MainWindow.xaml

Now we need to change the UI datagrid to use the new GroupedInventory properties.

On line 168, change the DataGrid’s ItemSource to “CurrentPlayer.GroupedInventory”.

Change the Paths for the DataGridTextColumns (lines 173 and 180) to include “Item.”, because GroupedInventory holds a complete GameItem object as its “Item” property, and we want to display the “Name” or “Price” property from “Item”.

I also added a new DataGridTextColumn on 175-178, to display the Quantity value.





Step 6: Modify WPFUI\TradeScreen.xaml and TradeScreen.xaml.cs

We also need to update the inventory datagrids on the trading screen. These are the same changes as we made for MainWindow.

Lines 40 and 71 need to use GroupedInventory, instead of Inventory.

Lines 51, 59, 86, and 94 need to have “Item.” Added to the property paths.

Add new DataGridTextColumns for the Quantity on lines 52-55 and 87-90.

Finally, change the button text to include “1”, so the player knows they are selling or buying one of the item, and not the total quantity of that item. The button changes are on lines 65 and 100.


In TradeScreen.xaml.cs, we need to change the buy and sell functions, because the datagrids now contain GroupedInventoryItem objects, instead of ItemQuantity objects.


In OnClick_Sell and OnClick_Buy, we need to cast the event sender to a GroupedInventoryItem object. Then, change the buying and selling code to use groupedInventoryItem.Item.Price and groupedInventoryItem.Item.








Step 7: Test the game

Fight some monsters, collect loot items, and visit the trader. make sure there are multiple lines for weapons, and only one line for each non-unique item (like snakeskins).


Return to main page

14 thoughts on “Lesson 10.2: Grouping GameItems in Inventories

  1. Not sure whether it is my mistake. But in line 93 or LivingEntity, I think it should be gi.Item.ItemTypeID == item.ItemTypeID. Becuase otherwise when I try to finish one quest, only one item is removed from inventory.

    1. This should be handled in the code that completes the quest:

      foreach (ItemQuantity itemQuantity in quest.ItemsToComplete)
      for(int i = 0; i < itemQuantity.Quantity; i++) { CurrentPlayer.RemoveItemFromInventory(CurrentPlayer.Inventory.First(item => item.ItemTypeID == itemQuantity.ItemID));

      If you need to turn in five snake skins, the inner loop calls RemoveItemFromInventory five times (the “for” loop would run five times, because itemQuantity.Quantity is 5). That loop finds the first item with a matching ItemTypeID and passes that complete item to the RemoveItemFromInventory function.

      When you complete a quest with multiple QuestCompletionItems, is the program not removing them all?

      1. Where you have RemoveItemFromInventory inside the LivingEntity, you only have it check for the gi.Item == Item. All this does is remove 1 item from your inventory when you try to complete your quest. Doing whatCuichen does fixes the issue

        1. Are you talking about the Inventory property, or the GroupedInventory property? For the Inventory property, we call it multiple times in the loop in the QuestCompletion code. So, it looks like it’s deleting everything properly when I tested it. Can you upload your version to gist.github.com (or some other place), so I can see exactly where you are talking about making the change?

  2. I have same problem like “cuichen li”. When i finish quest only one item is remove from inventory and i dont know why and what can i do to fix it

    1. Can you show me a screenshot of your inventory before and after turning in the quest? When I tested this, I do not see the problem. But, If I can see your exact inventory, I can try to reproduce the problem.

      1. Screenshot : https://imgur.com/a/AgslwXs

        When i debug code i see this

        is running normally but when it move to this

        first time work fine. But in 2nd loop code see this item = null and dont start code

        I think you understand me:D

        1. Thank you. After I finish the lessons for converting the factories to use XML files, I’ll do a lesson to fix this (maybe by changing how we do PropertyChanged notifications).

  3. Hi Scott,
    I have a question at LivingEntity.cs line 77

    if(!GroupedInventory.Any(gi => gi.Item.ItemTypeID == item.ItemTypeID))

    Can it be also

    if(!GroupedInventory.Any(gi => gi.Item == item))

    like this?

  4. Hi Scott!

    I think I found a missing part. 🙂

    In Gamesession.cs,

    foreach (GameItem gameItem in CurrentMonster.Inventory)
    RaiseMessage($”You obtained one {gameItem.Name}.”);


    –> CurrentPlayer.AddItemToInventory(gameItem);

    I think you forgot to mention this.
    Am I correct? If so, I am so happy. Because I am getting better because of your lecture!

  5. I noticed some runtime errors after implementing these changes. I didn’t roll back to see if they were present before but the Error is:

    The program seems to run with these errors. I believe the TradeScreen is causing the error. Does the ComboBox in MainWindow.xaml need to be refactored?


    1. I made a note to check the XAML bindings. After I post the final video for converting the game data to XML files, I’m going to work on bug fixes and general improvements to the program.

Leave a Reply

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