Now, we can move the classes from the Engine class into their new classes.
We’ll cut-and-paste the files, update the classes’ namespaces, and update the “using” directives.
The video will show me going through the actual move – in case it’s difficult to determine exactly what to do in the written steps below.
NOTE: When following the steps, make sure you copy files, when the step says FILE or FILES, and folders, where the step says FOLDERS.
Also, when you move the files, you really do not need to update the namespaces. You could leave the namespace unchanged. However, if you add new classes to the new projects, those classes will be created (by default, from Visual Studio) to use the new project’s name. So, some classes in that project would have the old namespace and some would have the new one.
Step 1: Move ViewModels
Cut-and-paste the GameSession.cs FILE from Engine\ViewModels into SOSCSRPG.ViewModels
Step 2: Move Services
Cut-and-paste the FILES from Engine\Services into SOSCSRPG.Services
Cut-and-paste the FOLDER Engine\Factories into SOSCSRPG.Services
Step 3: Move Models
Cut-and-paste the FILES from Engine\Models into SOSCSRPG.Models
Cut-and-paste the FOLDER Engine\Shared into SOSCSRPG.Models
Cut-and-paste the FOLDER Engine\Actions into SOSCSRPG.Models
Step 4: Update namespaces and using directives
Ctrl-Shift-H
Look in: “Entire solution”
File types: !*\bin\*;!*\obj\*;!*\.*
“Engine.Factories” to “SOSCSRPG.Services.Factories”
“Engine.Actions” to “SOSCSRPG.Models.Actions”
“Engine.Shared” to “SOSCSRPG.Models.Shared”
“assembly=Engine” to “assembly=SOSCSRPG.ViewModels”
“Engine.” to “SOSCSRPG.”
Step 5: Update assembly namespace in Startup.xaml
In this XAML Window, we are not binding to a ViewModel. We’re directly binding to a Model – GameDetails.
This is something we probably need to change in the future, to be consistent. But, for now, that means we need to change the xmlns and DataContext lines to use the SOSCSRPG.Models namespace and assembly.
Startup.xaml
<Window x:Class="WPFUI.Startup"
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"
xmlns:models="clr-namespace:SOSCSRPG.Models;assembly=SOSCSRPG.Models"
d:DataContext="{d:DesignInstance models:GameDetails}"
mc:Ignorable="d"
FontSize="11pt"
Title="{Binding Title}" Height="400" Width="400">
<Grid Margin="10,10,10,10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0"
Margin="0,5,0,5"
HorizontalAlignment="Center"
Width="125"
Content="Start new game"
Click="StartNewGame_OnClick"/>
<Button Grid.Row="1" Grid.Column="0"
Margin="0,5,0,5"
HorizontalAlignment="Center"
Width="125"
Content="Load saved game"
Click="LoadSavedGame_OnClick"/>
<Button Grid.Row="2" Grid.Column="0"
Margin="0,5,0,5"
HorizontalAlignment="Center"
Width="125"
Content="Exit"
Click="Exit_OnClick"/>
</Grid>
</Window>
Step 5: Make classes/functions visible
Change SOSCSRPG.Models\World’s AddLocation() to public
Change SOSCSRPG.Services\Factories\WorldFactory’s CreateWorld() to public
Change SOSCSRPG.Services\Factories\WorldFactory class to public
World.cs
using System.Collections.Generic;
namespace SOSCSRPG.Models
{
public class World
{
private readonly List<Location> _locations = new List<Location>();
public void AddLocation(Location location)
{
_locations.Add(location);
}
public Location LocationAt(int xCoordinate, int yCoordinate)
{
foreach(Location loc in _locations)
{
if(loc.XCoordinate == xCoordinate && loc.YCoordinate == yCoordinate)
{
return loc;
}
}
return null;
}
}
}
WorldFactory.cs
using System.IO;
using System.Xml;
using SOSCSRPG.Models;
using SOSCSRPG.Models.Shared;
namespace SOSCSRPG.Services.Factories
{
public static class WorldFactory
{
private const string GAME_DATA_FILENAME = ".\\GameData\\Locations.xml";
public static World CreateWorld()
{
World world = new World();
if(File.Exists(GAME_DATA_FILENAME))
{
XmlDocument data = new XmlDocument();
data.LoadXml(File.ReadAllText(GAME_DATA_FILENAME));
string rootImagePath =
data.SelectSingleNode("/Locations")
.AttributeAsString("RootImagePath");
LoadLocationsFromNodes(world,
rootImagePath,
data.SelectNodes("/Locations/Location"));
}
else
{
throw new FileNotFoundException($"Missing data file: {GAME_DATA_FILENAME}");
}
return world;
}
private static void LoadLocationsFromNodes(World world, string rootImagePath, XmlNodeList nodes)
{
if(nodes == null)
{
return;
}
foreach(XmlNode node in nodes)
{
Location location =
new Location(node.AttributeAsInt("X"),
node.AttributeAsInt("Y"),
node.AttributeAsString("Name"),
node.SelectSingleNode("./Description")?.InnerText ?? "",
$".{rootImagePath}{node.AttributeAsString("ImageName")}");
AddMonsters(location, node.SelectNodes("./Monsters/Monster"));
AddQuests(location, node.SelectNodes("./Quests/Quest"));
AddTrader(location, node.SelectSingleNode("./Trader"));
world.AddLocation(location);
}
}
private static void AddMonsters(Location location, XmlNodeList monsters)
{
if(monsters == null)
{
return;
}
foreach(XmlNode monsterNode in monsters)
{
location.AddMonster(monsterNode.AttributeAsInt("ID"),
monsterNode.AttributeAsInt("Percent"));
}
}
private static void AddQuests(Location location, XmlNodeList quests)
{
if(quests == null)
{
return;
}
foreach(XmlNode questNode in quests)
{
location.QuestsAvailableHere
.Add(QuestFactory.GetQuestByID(questNode.AttributeAsInt("ID")));
}
}
private static void AddTrader(Location location, XmlNode traderHere)
{
if(traderHere == null)
{
return;
}
location.TraderHere =
TraderFactory.GetTraderByID(traderHere.AttributeAsInt("ID"));
}
}
}
Step 6: Delete project references to Engine project
Delete the Engine project
Double-click on the WPFUI and SOSCSRPG.ViewModels projects and remove the ProjectReference lines to the Engine project
SOSCSRPG.ViewModels.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Fody" Version="6.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SOSCSRPG.Models\SOSCSRPG.Models.csproj" />
<ProjectReference Include="..\SOSCSRPG.Services\SOSCSRPG.Services.csproj" />
</ItemGroup>
</Project>
WPFUI.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SOSCSRPG.Core\SOSCSRPG.Core.csproj" />
<ProjectReference Include="..\SOSCSRPG.Models\SOSCSRPG.Models.csproj" />
<ProjectReference Include="..\SOSCSRPG.ViewModels\SOSCSRPG.ViewModels.csproj" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy $(SolutionDir)GameFiles\*.* $(ProjectDir)$(OutDir) /s /y" />
</Target>
</Project>
Step 8: Test game
NEXT LESSON: Lesson 20.1: Create floating inventory canvas
PREVIOUS LESSON: Lesson 19.13: Remove CombatService dependency from model classes