Now, we’ll add traders to the game, to let the player buy and sell items.
Step 1: Create Engine\Models\Trader.cs
First, we’ll create the Trader class. The Trader will have two properties: Name and Inventory (a List of GameItem objects).
We’ll add two functions: AddItemToInventory and RemoveItemFromInventory, which will be needed for the trader to “buy” and “sell” items.
Trader.cs
using System.Collections.ObjectModel;
namespace Engine.Models
{
public class Trader : BaseNotificationClass
{
public string Name { get; set; }
public ObservableCollection<GameItem> Inventory { get; set; }
public Trader(string name)
{
Name = name;
Inventory = new ObservableCollection<GameItem>();
}
public void AddItemToInventory(GameItem item)
{
Inventory.Add(item);
}
public void RemoveItemFromInventory(GameItem item)
{
Inventory.Remove(item);
}
}
}
Step 2: Create Engine\Factories\TraderFactory.cs
Next, we’ll add our factory class, to instantiate Trader objects.
The constructor will create three Trader objects, and add them to the _traders list. The AddTraderToList function will prevent adding a duplicate trader.
TraderFactory.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Engine.Models;
namespace Engine.Factories
{
public static class TraderFactory
{
private static readonly List<Trader> _traders = new List<Trader>();
static TraderFactory()
{
Trader susan = new Trader("Susan");
susan.AddItemToInventory(ItemFactory.CreateGameItem(1001));
Trader farmerTed = new Trader("Farmer Ted");
farmerTed.AddItemToInventory(ItemFactory.CreateGameItem(1001));
Trader peteTheHerbalist = new Trader("Pete the Herbalist");
peteTheHerbalist.AddItemToInventory(ItemFactory.CreateGameItem(1001));
AddTraderToList(susan);
AddTraderToList(farmerTed);
AddTraderToList(peteTheHerbalist);
}
public static Trader GetTraderByName(string name)
{
return _traders.FirstOrDefault(t => t.Name == name);
}
private static void AddTraderToList(Trader trader)
{
if(_traders.Any(t => t.Name == trader.Name))
{
throw new ArgumentException($"There is already a trader named '{trader.Name}'");
}
_traders.Add(trader);
}
}
}
Step 3: Modify Engine\Models\Location.cs
The traders will “live” at a location, so we need to add a “TraderHere” property to the Location class. This is on line 19, in the code below.
Location.cs
using System.Collections.Generic;
using System.Linq;
using Engine.Factories;
namespace Engine.Models
{
public class Location
{
public int XCoordinate { get; set; }
public int YCoordinate { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageName { get; set; }
public List<Quest> QuestsAvailableHere { get; set; } = new List<Quest>();
public List<MonsterEncounter> MonstersHere { get; set; } =
new List<MonsterEncounter>();
public Trader TraderHere { get; set; }
public void AddMonster(int monsterID, int chanceOfEncountering)
{
if(MonstersHere.Exists(m => m.MonsterID == monsterID))
{
// This monster has already been added to this location.
// So, overwrite the ChanceOfEncountering with the new number.
MonstersHere.First(m => m.MonsterID == monsterID)
.ChanceOfEncountering = chanceOfEncountering;
}
else
{
// This monster is not already at this location, so add it.
MonstersHere.Add(new MonsterEncounter(monsterID, chanceOfEncountering));
}
}
public Monster GetMonster()
{
if(!MonstersHere.Any())
{
return null;
}
// Total the percentages of all monsters at this location.
int totalChances = MonstersHere.Sum(m => m.ChanceOfEncountering);
// Select a random number between 1 and the total (in case the total chances is not 100).
int randomNumber = RandomNumberGenerator.NumberBetween(1, totalChances);
// Loop through the monster list,
// adding the monster's percentage chance of appearing to the runningTotal variable.
// When the random number is lower than the runningTotal,
// that is the monster to return.
int runningTotal = 0;
foreach(MonsterEncounter monsterEncounter in MonstersHere)
{
runningTotal += monsterEncounter.ChanceOfEncountering;
if(randomNumber <= runningTotal)
{
return MonsterFactory.GetMonster(monsterEncounter.MonsterID);
}
}
// If there was a problem, return the last monster in the list.
return MonsterFactory.GetMonster(MonstersHere.Last().MonsterID);
}
}
}
Step 4: Modify Engine\Factories\WorldFactory.cs
The location’s trader (if it has one) will be populated in the WorldFactory. Do this inside the CreateWorld function, with the other Location creation/population code.
There are three traders. The code for adding them is on lines 20-21, 30-31, and 50-51.
WorldFactory.cs
using Engine.Models;
namespace Engine.Factories
{
internal static class WorldFactory
{
internal static World CreateWorld()
{
World newWorld = new World();
newWorld.AddLocation(-2, -1, "Farmer's Field",
"There are rows of corn growing here, with giant rats hiding between them.",
"FarmFields.png");
newWorld.LocationAt(-2, -1).AddMonster(2, 100);
newWorld.AddLocation(-1, -1, "Farmer's House",
"This is the house of your neighbor, Farmer Ted.",
"Farmhouse.png");
newWorld.LocationAt(-1, -1).TraderHere =
TraderFactory.GetTraderByName("Farmer Ted");
newWorld.AddLocation(0, -1, "Home",
"This is your home",
"Home.png");
newWorld.AddLocation(-1, 0, "Trading Shop",
"The shop of Susan, the trader.",
"Trader.png");
newWorld.LocationAt(-1, 0).TraderHere =
TraderFactory.GetTraderByName("Susan");
newWorld.AddLocation(0, 0, "Town square",
"You see a fountain here.",
"TownSquare.png");
newWorld.AddLocation(1, 0, "Town Gate",
"There is a gate here, protecting the town from giant spiders.",
"TownGate.png");
newWorld.AddLocation(2, 0, "Spider Forest",
"The trees in this forest are covered with spider webs.",
"SpiderForest.png");
newWorld.LocationAt(2, 0).AddMonster(3, 100);
newWorld.AddLocation(0, 1, "Herbalist's hut",
"You see a small hut, with plants drying from the roof.",
"HerbalistsHut.png");
newWorld.LocationAt(0, 1).TraderHere =
TraderFactory.GetTraderByName("Pete the Herbalist");
newWorld.LocationAt(0, 1).QuestsAvailableHere.Add(QuestFactory.GetQuestByID(1));
newWorld.AddLocation(0, 2, "Herbalist's garden",
"There are many plants here, with snakes hiding behind them.",
"HerbalistsGarden.png");
newWorld.LocationAt(0, 2).AddMonster(1, 100);
return newWorld;
}
}
}
Step 5: Modify Engine\ViewModels\GameSession.cs
We’ll update the GameSession ViewModel class to let the UI know if there is a trader at the player’s current location.
Add a new property CurrentTrader (lines 61-71) and its backing variable “_currentTrader” (line 17).
CurrentTrader will be set when the player moves to a new location. Do this inside the CurrentLocation setter (line 39).
Create a Boolean property “HasTrader” (line 89), to use for the UI to decide whether or not to display the “Trade” button – based on whether or not there is a CurrentTrader.
GameSession.cs
using System;
using System.Linq;
using Engine.EventArgs;
using Engine.Factories;
using Engine.Models;
namespace Engine.ViewModels
{
public class GameSession : BaseNotificationClass
{
public event EventHandler<GameMessageEventArgs> OnMessageRaised;
#region Properties
private Location _currentLocation;
private Monster _currentMonster;
private Trader _currentTrader;
public World CurrentWorld { get; set; }
public Player CurrentPlayer { get; set; }
public Location CurrentLocation
{
get { return _currentLocation; }
set
{
_currentLocation = value;
OnPropertyChanged(nameof(CurrentLocation));
OnPropertyChanged(nameof(HasLocationToNorth));
OnPropertyChanged(nameof(HasLocationToEast));
OnPropertyChanged(nameof(HasLocationToWest));
OnPropertyChanged(nameof(HasLocationToSouth));
CompleteQuestsAtLocation();
GivePlayerQuestsAtLocation();
GetMonsterAtLocation();
CurrentTrader = CurrentLocation.TraderHere;
}
}
public Monster CurrentMonster
{
get { return _currentMonster; }
set
{
_currentMonster = value;
OnPropertyChanged(nameof(CurrentMonster));
OnPropertyChanged(nameof(HasMonster));
if (CurrentMonster != null)
{
RaiseMessage("");
RaiseMessage($"You see a {CurrentMonster.Name} here!");
}
}
}
public Trader CurrentTrader
{
get { return _currentTrader; }
set
{
_currentTrader = value;
OnPropertyChanged(nameof(CurrentTrader));
OnPropertyChanged(nameof(HasTrader));
}
}
public Weapon CurrentWeapon { get; set; }
public bool HasLocationToNorth =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate, CurrentLocation.YCoordinate + 1) != null;
public bool HasLocationToEast =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate + 1, CurrentLocation.YCoordinate) != null;
public bool HasLocationToSouth =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate, CurrentLocation.YCoordinate - 1) != null;
public bool HasLocationToWest =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate - 1, CurrentLocation.YCoordinate) != null;
public bool HasMonster => CurrentMonster != null;
public bool HasTrader => CurrentTrader != null;
#endregion
public GameSession()
{
CurrentPlayer = new Player
{
Name = "Scott",
CharacterClass = "Fighter",
HitPoints = 10,
Gold = 1000000,
ExperiencePoints = 0,
Level = 1
};
if (!CurrentPlayer.Weapons.Any())
{
CurrentPlayer.AddItemToInventory(ItemFactory.CreateGameItem(1001));
}
CurrentWorld = WorldFactory.CreateWorld();
CurrentLocation = CurrentWorld.LocationAt(0, 0);
}
public void MoveNorth()
{
if(HasLocationToNorth)
{
CurrentLocation = CurrentWorld.LocationAt(CurrentLocation.XCoordinate, CurrentLocation.YCoordinate + 1);
}
}
public void MoveEast()
{
if(HasLocationToEast)
{
CurrentLocation = CurrentWorld.LocationAt(CurrentLocation.XCoordinate + 1, CurrentLocation.YCoordinate);
}
}
public void MoveSouth()
{
if(HasLocationToSouth)
{
CurrentLocation = CurrentWorld.LocationAt(CurrentLocation.XCoordinate, CurrentLocation.YCoordinate - 1);
}
}
public void MoveWest()
{
if(HasLocationToWest)
{
CurrentLocation = CurrentWorld.LocationAt(CurrentLocation.XCoordinate - 1, CurrentLocation.YCoordinate);
}
}
private void CompleteQuestsAtLocation()
{
foreach(Quest quest in CurrentLocation.QuestsAvailableHere)
{
QuestStatus questToComplete =
CurrentPlayer.Quests.FirstOrDefault(q => q.PlayerQuest.ID == quest.ID &&
!q.IsCompleted);
if(questToComplete != null)
{
if(CurrentPlayer.HasAllTheseItems(quest.ItemsToComplete))
{
// Remove the quest completion items from the player's inventory
foreach (ItemQuantity itemQuantity in quest.ItemsToComplete)
{
for(int i = 0; i < itemQuantity.Quantity; i++)
{
CurrentPlayer.RemoveItemFromInventory(CurrentPlayer.Inventory.First(item => item.ItemTypeID == itemQuantity.ItemID));
}
}
RaiseMessage("");
RaiseMessage($"You completed the '{quest.Name}' quest");
// Give the player the quest rewards
CurrentPlayer.ExperiencePoints += quest.RewardExperiencePoints;
RaiseMessage($"You receive {quest.RewardExperiencePoints} experience points");
CurrentPlayer.Gold += quest.RewardGold;
RaiseMessage($"You receive {quest.RewardGold} gold");
foreach(ItemQuantity itemQuantity in quest.RewardItems)
{
GameItem rewardItem = ItemFactory.CreateGameItem(itemQuantity.ItemID);
CurrentPlayer.AddItemToInventory(rewardItem);
RaiseMessage($"You receive a {rewardItem.Name}");
}
// Mark the Quest as completed
questToComplete.IsCompleted = true;
}
}
}
}
private void GivePlayerQuestsAtLocation()
{
foreach(Quest quest in CurrentLocation.QuestsAvailableHere)
{
if(!CurrentPlayer.Quests.Any(q => q.PlayerQuest.ID == quest.ID))
{
CurrentPlayer.Quests.Add(new QuestStatus(quest));
RaiseMessage("");
RaiseMessage($"You receive the '{quest.Name}' quest");
RaiseMessage(quest.Description);
RaiseMessage("Return with:");
foreach(ItemQuantity itemQuantity in quest.ItemsToComplete)
{
RaiseMessage($" {itemQuantity.Quantity} {ItemFactory.CreateGameItem(itemQuantity.ItemID).Name}");
}
RaiseMessage("And you will receive:");
RaiseMessage($" {quest.RewardExperiencePoints} experience points");
RaiseMessage($" {quest.RewardGold} gold");
foreach(ItemQuantity itemQuantity in quest.RewardItems)
{
RaiseMessage($" {itemQuantity.Quantity} {ItemFactory.CreateGameItem(itemQuantity.ItemID).Name}");
}
}
}
}
private void GetMonsterAtLocation()
{
CurrentMonster = CurrentLocation.GetMonster();
}
public void AttackCurrentMonster()
{
if (CurrentWeapon == null)
{
RaiseMessage("You must select a weapon, to attack.");
return;
}
// Determine damage to monster
int damageToMonster = RandomNumberGenerator.NumberBetween(CurrentWeapon.MinimumDamage, CurrentWeapon.MaximumDamage);
if (damageToMonster == 0)
{
RaiseMessage($"You missed the {CurrentMonster.Name}.");
}
else
{
CurrentMonster.HitPoints -= damageToMonster;
RaiseMessage($"You hit the {CurrentMonster.Name} for {damageToMonster} points.");
}
// If monster if killed, collect rewards and loot
if(CurrentMonster.HitPoints <= 0)
{
RaiseMessage("");
RaiseMessage($"You defeated the {CurrentMonster.Name}!");
CurrentPlayer.ExperiencePoints += CurrentMonster.RewardExperiencePoints;
RaiseMessage($"You receive {CurrentMonster.RewardExperiencePoints} experience points.");
CurrentPlayer.Gold += CurrentMonster.RewardGold;
RaiseMessage($"You receive {CurrentMonster.RewardGold} gold.");
foreach(ItemQuantity itemQuantity in CurrentMonster.Inventory)
{
GameItem item = ItemFactory.CreateGameItem(itemQuantity.ItemID);
CurrentPlayer.AddItemToInventory(item);
RaiseMessage($"You receive {itemQuantity.Quantity} {item.Name}.");
}
// Get another monster to fight
GetMonsterAtLocation();
}
else
{
// If monster is still alive, let the monster attack
int damageToPlayer = RandomNumberGenerator.NumberBetween(CurrentMonster.MinimumDamage, CurrentMonster.MaximumDamage);
if (damageToPlayer == 0)
{
RaiseMessage("The monster attacks, but misses you.");
}
else
{
CurrentPlayer.HitPoints -= damageToPlayer;
RaiseMessage($"The {CurrentMonster.Name} hit you for {damageToPlayer} points.");
}
// If player is killed, move them back to their home.
if (CurrentPlayer.HitPoints <= 0)
{
RaiseMessage("");
RaiseMessage($"The {CurrentMonster.Name} killed you.");
CurrentLocation = CurrentWorld.LocationAt(0, -1); // Player's home
CurrentPlayer.HitPoints = CurrentPlayer.Level * 10; // Completely heal the player
}
}
}
private void RaiseMessage(string message)
{
OnMessageRaised?.Invoke(this, new GameMessageEventArgs(message));
}
}
}
Step 6: Modify WPFUI\MainWindow.xaml
Finally, add a “Trade” button to the UI (lines 264-267). Its Visibility will be based on the Boolean HasTrader property in GameSession.cs.
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:viewModels="clr-namespace:Engine.ViewModels;assembly=Engine"
d:DataContext="{d:DesignInstance viewModels:GameSession}"
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>
<!-- Menu -->
<Label Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Content="Menu" Background="AliceBlue"/>
<!-- Player stats -->
<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>
<!-- Gameplay -->
<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>
<!-- Game Messages -->
<Border Grid.Row="0" Grid.Column="0"
Grid.RowSpan="2"
BorderBrush="Gainsboro"
BorderThickness="1">
<RichTextBox x:Name="GameMessages"
Background="Beige"
VerticalScrollBarVisibility="Auto">
<RichTextBox.Resources>
<Style TargetType="{x:Type Paragraph}">
<Setter Property="Margin" Value="0"/>
</Style>
</RichTextBox.Resources>
</RichTextBox>
</Border>
<!-- Location information -->
<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>
<!-- Monster information -->
<Border Grid.Row="1" Grid.Column="1"
BorderBrush="Gainsboro"
BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
HorizontalAlignment="Center"
Height="Auto"
Text="{Binding CurrentMonster.Name}" />
<Image Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Height="125"
Width="125"
Source="{Binding CurrentMonster.ImageName}" />
<StackPanel Grid.Row="2"
Visibility="{Binding HasMonster, Converter={StaticResource BooleanToVisibility}}"
HorizontalAlignment="Center"
Orientation="Horizontal">
<TextBlock>Current Hit Points:</TextBlock>
<TextBlock Text="{Binding CurrentMonster.HitPoints}" />
</StackPanel>
</Grid>
</Border>
</Grid>
<!-- Inventory and Quests -->
<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>
<TabItem Header="Quests">
<DataGrid ItemsSource="{Binding CurrentPlayer.Quests}"
AutoGenerateColumns="False"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding PlayerQuest.Name}"
Width="*"/>
<DataGridTextColumn Header="Done?"
Binding="{Binding IsCompleted}"
Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
</TabControl>
</Grid>
<!-- Action controls -->
<Grid Grid.Row="2" Grid.Column="1"
Background="Lavender">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="255" />
</Grid.ColumnDefinitions>
<!-- Combat Controls -->
<Grid Grid.Row="0" Grid.Column="0"
Visibility="{Binding HasMonster, Converter={StaticResource BooleanToVisibility}}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<ComboBox Grid.Row="0" Grid.Column="0"
ItemsSource="{Binding CurrentPlayer.Weapons}"
SelectedItem="{Binding CurrentWeapon}"
DisplayMemberPath="Name"
SelectedValuePath="ID"/>
<Button Grid.Row="0" Grid.Column="2"
Content="Use"
Click="OnClick_AttackMonster"/>
</Grid>
<!-- Movement Controls -->
<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="1"
Height="25" Width="65" Margin="10"
Visibility="{Binding HasTrader, Converter={StaticResource BooleanToVisibility}}"
Content="Trade"/>
<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 7: Test the game
Run the game and move to the locations that should have a trader (the Farmer’s House, the Trading Shop, and the Herbalist’s Hut). You should see the “Trade” button at those locations, and not see it at the other locations.
NEXT LESSON: Lesson 09.2: Adding the Trade Screen
PREVIOUS LESSON: Lesson 08.1: Completing Quests
Spotted a couple of issues:
1) In the source code listing, WorldFactory.cs is missing a line: “using Engine.Models;” (so the World class is undefined).
2) In the video, from 3:48 you describe changes to the game session class, but omit the changes to line 39 in GameSession.cs: “CurrentTrader = CurrentLocation.TraderHere;”. Missing this results in the trade button not appearing because the current trader is not set when entering a new location.
Thanks Dave. I’ll check this out Saturday and see if I can correct this.