Press "Enter" to skip to content

Lesson 19.6: Move files out of Engine project

Some of our classes in the Engine project can be moved to their new projects because they don’t have any dependencies that would prevent that. So, we’ll move them now.

Like most of these refactoring steps, this isn’t a huge change. But it’s simple to do and it moves us towards our goal.

Step 1: Add project references

Because we’re moving files to new projects, we’ll need to add two new references – to make the classes visible to the other classes that use them.

Add a project reference:

  1. From Engine project to SOSCSRPG.Models project
  2. From SOSCSRPG.ViewModels to Engine

Step 2: Move CharacterCreationViewModel to SOSCSRPG.ViewModels project

Cut-and-paste from Engine\ViewModels

Update its namespace to SOSCSRPG.ViewModels (line 8)

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using Engine.Factories;
using Engine.Models;
using Engine.Services;
namespace SOSCSRPG.ViewModels
{
    public class CharacterCreationViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public GameDetails GameDetails { get; }
        public Race SelectedRace { get; init; }
        public string Name { get; init; }
        public ObservableCollection<PlayerAttribute> PlayerAttributes { get; } =
            new ObservableCollection<PlayerAttribute>();
        public bool HasRaces =>
            GameDetails.Races.Any();
        public bool HasRaceAttributeModifiers =>
            HasRaces && GameDetails.Races.Any(r => r.PlayerAttributeModifiers.Any());
        public CharacterCreationViewModel()
        {
            GameDetails = GameDetailsService.ReadGameDetails();
            if(HasRaces)
            {
                SelectedRace = GameDetails.Races.First();
            }
            
            RollNewCharacter();
        }
        public void RollNewCharacter()
        {
            PlayerAttributes.Clear();
            foreach(PlayerAttribute playerAttribute in GameDetails.PlayerAttributes)
            {
                playerAttribute.ReRoll();
                PlayerAttributes.Add(playerAttribute);
            }
            
            ApplyAttributeModifiers();
        }
        public void ApplyAttributeModifiers()
        {
            foreach(PlayerAttribute playerAttribute in PlayerAttributes)
            {
                var attributeRaceModifier =
                    SelectedRace.PlayerAttributeModifiers
                                .FirstOrDefault(pam => pam.AttributeKey.Equals(playerAttribute.Key));
                playerAttribute.ModifiedValue = 
                    playerAttribute.BaseValue + (attributeRaceModifier?.Modifier ?? 0);
            }
        }
        public Player GetPlayer()
        {
            Player player = new Player(Name, 0, 10, 10, PlayerAttributes, 10);
            // Give player default inventory items, weapons, recipes, etc.
            player.AddItemToInventory(ItemFactory.CreateGameItem(1001));
            player.AddItemToInventory(ItemFactory.CreateGameItem(2001));
            player.LearnRecipe(RecipeFactory.RecipeByID(1));
            player.AddItemToInventory(ItemFactory.CreateGameItem(3001));
            player.AddItemToInventory(ItemFactory.CreateGameItem(3002));
            player.AddItemToInventory(ItemFactory.CreateGameItem(3003));
            return player;
        }
    }
}

Step 3: Update namespace in WPFUI\CharacterCreation.xaml

Change the xmlns on line 7 to use the new location of the CharacterCreationViewModel class.

<Window x:Class="WPFUI.CharacterCreation"
        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:viewModels="clr-namespace:SOSCSRPG.ViewModels;assembly=SOSCSRPG.ViewModels"
        d:DataContext="{d:DesignInstance viewModels:CharacterCreationViewModel}"
        mc:Ignorable="d"
        FontSize="11pt"
        Title="{Binding GameDetails.Title}" Height="400" Width="400">
    <Grid Margin="10,10,10,10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Row="0" Grid.Column="0"
               Grid.ColumnSpan="2"
               FontWeight="Bold"
               HorizontalAlignment="Center"
               Content="Create a new character"/>
        <!-- Character creation controls -->
        <Label Grid.Row="1" Grid.Column="0"
               FontWeight="Bold"
               Content="Name:"/>
        <TextBox Grid.Row="1" Grid.Column="1"
                 Width="250"
                 HorizontalAlignment="Left"
                 Text="{Binding Name}"/>
        <Label Grid.Row="2" Grid.Column="0"
               FontWeight="Bold"
               Content="Race:"
               Visibility="{Binding HasRaces, Converter={StaticResource BooleanToVisibilityConverter}}"/>
        <ComboBox Grid.Row="2" Grid.Column="1"
                  Width="250"
                  HorizontalAlignment="Left"
                  ItemsSource="{Binding GameDetails.Races}"
                  DisplayMemberPath="DisplayName"
                  SelectedItem="{Binding SelectedRace, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                  SelectionChanged="Race_OnSelectionChanged"
                  Visibility="{Binding HasRaces, Converter={StaticResource BooleanToVisibilityConverter}}"/>
        <DataGrid Grid.Row="3" Grid.Column="0"
                  Grid.ColumnSpan="2"
                  ItemsSource="{Binding PlayerAttributes}"
                  AutoGenerateColumns="False"
                  CanUserAddRows="False"
                  CanUserDeleteRows="False"
                  HeadersVisibility="Column">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Attribute"
                                    Binding="{Binding DisplayName}"
                                    Width="*"/>
                <DataGridTextColumn Header="Value"
                                    Binding="{Binding BaseValue}"/>
                <DataGridTextColumn Header="Modified"
                                    Binding="{Binding ModifiedValue}"
                                    Visibility="{Binding HasRaceAttributeModifiers, 
                                                 Converter={StaticResource BooleanToVisibilityConverter}}"/>
            </DataGrid.Columns>
        </DataGrid>
        <Grid Grid.Row="4" Grid.Column="0"
                  Grid.ColumnSpan="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="10"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Button Grid.Row="0" Grid.Column="0"
                    Margin="0,5,0,5"
                    HorizontalAlignment="Center"
                    Width="125"
                    Content="Roll new player"
                    Click="RandomPlayer_OnClick"/>
            <Button Grid.Row="0" Grid.Column="2"
                    Margin="0,5,0,5"
                    HorizontalAlignment="Center"
                    Width="125"
                    Content="Use this player"
                    Click="UseThisPlayer_OnClick"/>
        </Grid>
    </Grid>
</Window>

Step 4: Update CharacterCreation.xaml.cs

Change the using directive on line 3 from “using Engine.ViewModels;” to “using SOSCSRPG.ViewModels;”

using System.Windows;
using System.Windows.Controls;
using SOSCSRPG.ViewModels;
namespace WPFUI
{
    public partial class CharacterCreation : Window
    {
        private CharacterCreationViewModel VM { get; set; }
        public CharacterCreation()
        {
            InitializeComponent();
            VM = new CharacterCreationViewModel();
            DataContext = VM;
        }
        private void RandomPlayer_OnClick(object sender, RoutedEventArgs e)
        {
            VM.RollNewCharacter();
        }
        private void UseThisPlayer_OnClick(object sender, RoutedEventArgs e)
        {
            MainWindow mainWindow = new MainWindow(VM.GetPlayer());
            mainWindow.Show();
            Close();
        }
        private void Race_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            VM.ApplyAttributeModifiers();
        }
    }
}

Step 5: Move Engine\EventArgs folder to SOSCSRPG.Models

Cut-and-paste the EventArgs folder, which should bring over CombatVictoryEventArgs.cs and GameMessageEventArgs.cs.

In both these classes, update their namespaces to “SOSCSRPG.Models.EventArgs”.

namespace SOSCSRPG.Models.EventArgs
{
    public class CombatVictoryEventArgs : System.EventArgs
    {
    }
}
namespace SOSCSRPG.Models.EventArgs
{
    public class GameMessageEventArgs : System.EventArgs
    {
        public string Message { get; private set; }
        public GameMessageEventArgs(string message)
        {
            Message = message;
        }
    }
}

Step 6: Update using directives for classes that use the EventArgs

Replace “using Event.EventArgs;” with “using SOSCSRPG.Models.EventArgs;” in :

  1. Engine\Models\Battle.cs
  2. Engine\Services\MessageBroker.cs
  3. WPFUI\MainWindow.xaml.cs

Step 7: Test the game

NEXT LESSON: Lesson 19.7: Decouple services from ItemQuantity

PREVIOUS LESSON: Lesson 19.5: Convert properties to auto-properties

3 Comments

  1. Johan
    Johan 2022-05-06

    I tried to follow this part, but VS2022 does not let me add project references to both engine and viewmodel, give an error about reccurency issues.

  2. Johan
    Johan 2022-05-06

    Solved it, i missread the info 😛 it was Engine => Models and Viewmodels => Engine.

    Silly me 😛

    • SOSCSRPG
      SOSCSRPG 2022-05-07

      No problem, we’ve all done that. 🙂

Leave a Reply

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