Press "Enter" to skip to content

Lesson 05.2: Creating the Player Inventory

Now that we can create items, it’s time to create the player’s inventory. In this lesson, we will add an Inventory property to the Player class, and display the inventory items in the UI.

Summary

Step 1: Edit Engine\Player.cs

The first thing we’ll do is create a new property in the Player class, to hold the GameItems the Player will have in their inventory.

In the ItemFactory class, we used a List<GameItem> variable to hold multiple GameItem objects. For the Player’s Inventory, we are going to use an ObservableCollection<GameItem> property. Collections are similar to Lists, and can hold zero, one, or many objects. However, an ObservableCollection also automatically notifies the UI when objects are added to it, or removed from it. Because of this, we don’t need to call “OnPropertyChanged” every time we modify the item’s in the player’s inventory.

To use ObservableCollection, we need to add “using System.Collections.ObjectModel;”

We also need to initialize the property (like we do for all list/collection properties). We do this by creating a constructor for the Player class, and initializing the property to a “new ObservableCollection<GameItem>()”. Now, when a Player object is created, the Inventory property will be initialized, and the player can start receiving items.

Player.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Engine.Models
{
    public class Player : BaseNotificationClass
    {
        private string _name;
        private string _characterClass;
        private int _hitPoints;
        private int _experiencePoints;
        private int _level;
        private int _gold;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value; 
                OnPropertyChanged(nameof(Name));
            }
        }
        public string CharacterClass
        {
            get { return _characterClass; }
            set
            {
                _characterClass = value; 
                OnPropertyChanged(nameof(CharacterClass));
            }
        }
        public int HitPoints
        {
            get { return _hitPoints; }
            set
            {
                _hitPoints = value; 
                OnPropertyChanged(nameof(HitPoints));
            }
        }
        public int ExperiencePoints
        {
            get { return _experiencePoints; }
            set
            {
                _experiencePoints = value; 
                OnPropertyChanged(nameof(ExperiencePoints));
            }
        }
        public int Level
        {
            get { return _level; }
            set
            {
                _level = value; 
                OnPropertyChanged(nameof(Level));
            }
        }
        public int Gold
        {
            get { return _gold; }
            set
            {
                _gold = value;
                OnPropertyChanged(nameof(Gold));
            }
        }
        public ObservableCollection<GameItem> Inventory { get; set; }
        public Player()
        {
            Inventory = new ObservableCollection<GameItem>();
        }
    }
}

Step 2: Edit WPFUI\MainWindow.cs

Currently, we have a Label that says “Inventory/Quests”, for Grid.Row=”2″ and Grid.Column=”0″ (around line 102, in MainWindow.xaml). We are going to replace it with a Grid.

Inside the Grid, we add a new type of UI control – a TabControl. This is a control that looks like a paper file folders (like you would store in a desk drawer), with tabs at different positions.

Inside the TabControl, you create a TabItem for each “tab”. The TabItem’s Header attribute is the text to display.

Inside the TabItem, we will add a DataGrid. This is a popular control for displaying information that has multiple rows (like lists and collections) and multiple columns (in this case, properties we want to display for the objects in the list/collection).

The DataGrid’s ItemsSource attribute is the list/collection to display. We also want to set the “AutoGenerateColumns” attribute to “False”. By default, a datagrid will create a column for each property of the object in the list/collection. However, we want to select the properties to display, and how we want to display them. Finally, we want to set the DataGrid’s “HeadersVisibility” attribute to “Column”. So it will only show column name headers, and not row headers.

Between the DataGrid opening and closing tags, we need the tags for “DataGrid.Columns”, since this is where we will define the details for the columns.

We use two DataGridTextColumns, because we want to display text values. There are other columns types you can use to display checkboxes (DataGridCheckBoxColumn), dropdown boxes (DataGridComboBoxColumn), text links (DataGridHyperlinkColumn), and customized content (DataGridTemplateColumn). But, for this DataGrid, we only need to use DataGridTextColumn.

For the attributes of the DataGridTextColumn, we set the Header (the text to display at the top of the column), the property to bind to (from the GameItem objects, in the DataGrid’s ItemsSource), and the width.

MainWindow.xaml
<Window x:Class="WPFUI.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFUI"
        mc:Ignorable="d"
        FontSize="11pt"
        Title="Scott's Awesome Game" Height="768" Width="1024">
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="225"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="250"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Content="Menu" Background="AliceBlue"/>
        <Grid Grid.Row="1" Grid.Column="0" Background="Aquamarine">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            
            <Label Grid.Row="0" Grid.Column="0" Content="Name:"/>
            <Label Grid.Row="0" Grid.Column="1" Content="{Binding CurrentPlayer.Name}"/>
            <Label Grid.Row="1" Grid.Column="0" Content="Class:"/>
            <Label Grid.Row="1" Grid.Column="1" Content="{Binding CurrentPlayer.CharacterClass}"/>
            <Label Grid.Row="2" Grid.Column="0" Content="Hit points:"/>
            <Label Grid.Row="2" Grid.Column="1" Content="{Binding CurrentPlayer.HitPoints}"/>
            <Label Grid.Row="3" Grid.Column="0" Content="Gold:"/>
            <Label Grid.Row="3" Grid.Column="1" Content="{Binding CurrentPlayer.Gold}"/>
            <Label Grid.Row="4" Grid.Column="0" Content="XP:"/>
            <Label Grid.Row="4" Grid.Column="1" Content="{Binding CurrentPlayer.ExperiencePoints}"/>
            <Label Grid.Row="5" Grid.Column="0" Content="Level:"/>
            <Label Grid.Row="5" Grid.Column="1" Content="{Binding CurrentPlayer.Level}"/>
        </Grid>
        <Grid Grid.Row="1" Grid.Column="1"
              Background="Beige">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3*"/>
                <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
            
            <Border Grid.Row="0" Grid.Column="1"
                    BorderBrush="Gainsboro"
                    BorderThickness="1">
                
                <Grid Margin="3">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    
                    <TextBlock Grid.Row="0"
                               HorizontalAlignment="Center"
                               Text="{Binding CurrentLocation.Name}"/>
                    
                    <Image Grid.Row="1"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           Height="125"
                           Width="125"
                           Source="{Binding CurrentLocation.ImageName}"/>
                    
                    <TextBlock Grid.Row="2"
                               HorizontalAlignment="Center"
                               Text="{Binding CurrentLocation.Description}"
                               TextWrapping="Wrap"/>
                </Grid>
                
            </Border>
        </Grid>
        <Grid Grid.Row="2" Grid.Column="0"
              Background="BurlyWood">
        
            <TabControl>
                <TabItem Header="Inventory">
                    <DataGrid ItemsSource="{Binding CurrentPlayer.Inventory}"
                              AutoGenerateColumns="False"
                              HeadersVisibility="Column">
                        <DataGrid.Columns>
                            <DataGridTextColumn Header="Description"
                                                Binding="{Binding Name}"
                                                Width="*"/>
                            <DataGridTextColumn Header="Price"
                                                Binding="{Binding Price}"
                                                Width="Auto"/>
                        </DataGrid.Columns>
                    </DataGrid>
                </TabItem>
            </TabControl>
        </Grid>
        <Grid Grid.Row="2" Grid.Column="1"
              Background="Lavender">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="255" />
            </Grid.ColumnDefinitions>
            
            <Grid Grid.Row="0" Grid.Column="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Button Grid.Row="0" Grid.Column="1" 
                        Height="25" Width="65" Margin="10" 
                        Click="OnClick_MoveNorth"
                        Visibility="{Binding HasLocationToNorth, Converter={StaticResource BooleanToVisibility}}"
                        Content="North"/>
                <Button Grid.Row="1" Grid.Column="0" 
                        Height="25" Width="65" Margin="10" 
                        Click="OnClick_MoveWest"
                        Visibility="{Binding HasLocationToWest, Converter={StaticResource BooleanToVisibility}}"
                        Content="West"/>
                <Button Grid.Row="1" Grid.Column="2" 
                        Height="25" Width="65" Margin="10" 
                        Click="OnClick_MoveEast"
                        Visibility="{Binding HasLocationToEast, Converter={StaticResource BooleanToVisibility}}"
                        Content="East"/>
                <Button Grid.Row="2" Grid.Column="1" 
                        Height="25" Width="65" Margin="10" 
                        Click="OnClick_MoveSouth"
                        Visibility="{Binding HasLocationToSouth, Converter={StaticResource BooleanToVisibility}}"
                        Content="South"/>
            </Grid>
        </Grid>
    </Grid>
</Window>

Step 3: Test, by modifying GameSession.cs

If you want to test the game, and ensure the player’s inventory is displayed, you can modify the GameSession constructor to this code below, with the three lines adding items to CurrentPlayer.Inventory.

GameSession.cs
public GameSession()
{
    CurrentPlayer = new Player
                    {
                        Name = "Scott",
                        CharacterClass = "Fighter",
                        HitPoints = 10,
                        Gold = 1000000,
                        ExperiencePoints = 0,
                        Level = 1
                    };
    CurrentWorld = WorldFactory.CreateWorld();
    CurrentLocation = CurrentWorld.LocationAt(0, 0);
    CurrentPlayer.Inventory.Add(ItemFactory.CreateGameItem(1001));
    CurrentPlayer.Inventory.Add(ItemFactory.CreateGameItem(1001));
    CurrentPlayer.Inventory.Add(ItemFactory.CreateGameItem(1002));
}

If you add these lines, and run the game, you should see items in the DataGrid for the Player’s Inventory.

After you test that this works, please remove the lines from the GameSesssion constructor (or remember to do that later).

NEXT LESSON: Lesson 06.1: Creating the Quest Factory

PREVIOUS LESSON: Lesson 05.1: Creating the Game Item Factory

    Leave a Reply

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