Skip to content

Instantly share code, notes, and snippets.

@ScottLilly
Last active July 26, 2022 00:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ScottLilly/f3ffb55c2fd8ea87204d839cee39be45 to your computer and use it in GitHub Desktop.
Save ScottLilly/f3ffb55c2fd8ea87204d839cee39be45 to your computer and use it in GitHub Desktop.
Lesson 18.4: Adding Player Attributes to Living Entities
using System.Collections.ObjectModel;
using System.Linq;
using Engine.Factories;
using Engine.Models;
using Engine.Services;
namespace Engine.ViewModels
{
public class CharacterCreationViewModel : BaseNotificationClass
{
private Race _selectedRace;
public GameDetails GameDetails { get; }
public Race SelectedRace
{
get => _selectedRace;
set
{
_selectedRace = value;
OnPropertyChanged();
}
}
public string Name { get; set; }
public ObservableCollection<PlayerAttribute> PlayerAttributes { get; set; } =
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;
}
}
}
using Engine.Models;
using Engine.Shared;
namespace Engine.Services
{
public static class CombatService
{
public enum Combatant
{
Player,
Opponent
}
public static Combatant FirstAttacker(Player player, Monster opponent)
{
// Formula is: ((Dex(player)^2 - Dex(monster)^2)/10) + Random(-10/10)
// For dexterity values from 3 to 18, this should produce an offset of +/- 41.5
int playerDexterity = player.GetAttribute("DEX").ModifiedValue *
player.GetAttribute("DEX").ModifiedValue;
int opponentDexterity = opponent.GetAttribute("DEX").ModifiedValue *
opponent.GetAttribute("DEX").ModifiedValue;
decimal dexterityOffset = (playerDexterity - opponentDexterity) / 10m;
int randomOffset = DiceService.Instance.Roll(20).Value - 10;
decimal totalOffset = dexterityOffset + randomOffset;
return DiceService.Instance.Roll(100).Value <= 50 + totalOffset
? Combatant.Player
: Combatant.Opponent;
}
public static bool AttackSucceeded(LivingEntity attacker, LivingEntity target)
{
// Currently using the same formula as FirstAttacker initiative.
// This will change as we include attack/defense skills,
// armor, weapon bonuses, enchantments/curses, etc.
int playerDexterity = attacker.GetAttribute("DEX").ModifiedValue *
attacker.GetAttribute("DEX").ModifiedValue;
int opponentDexterity = target.GetAttribute("DEX").ModifiedValue *
target.GetAttribute("DEX").ModifiedValue;
decimal dexterityOffset = (playerDexterity - opponentDexterity) / 10m;
int randomOffset = DiceService.Instance.Roll(20).Value - 10;
decimal totalOffset = dexterityOffset + randomOffset;
return DiceService.Instance.Roll(100).Value <= 50 + totalOffset;
}
}
}
using System;
using System.Linq;
using System.Xml;
using Engine.Models;
using Newtonsoft.Json.Linq;
namespace Engine.Shared
{
public static class ExtensionMethods
{
public static int AttributeAsInt(this XmlNode node, string attributeName)
{
return Convert.ToInt32(node.AttributeAsString(attributeName));
}
public static string AttributeAsString(this XmlNode node, string attributeName)
{
XmlAttribute attribute = node.Attributes?[attributeName];
if(attribute == null)
{
throw new ArgumentException($"The attribute '{attributeName}' does not exist");
}
return attribute.Value;
}
public static string StringValueOf(this JObject jsonObject, string key)
{
return jsonObject[key].ToString();
}
public static string StringValueOf(this JToken jsonToken, string key)
{
return jsonToken[key].ToString();
}
public static int IntValueOf(this JToken jsonToken, string key)
{
return Convert.ToInt32(jsonToken[key]);
}
public static PlayerAttribute GetAttribute(this LivingEntity entity, string attributeKey)
{
return entity.Attributes
.First(pa => pa.Key.Equals(attributeKey,
StringComparison.CurrentCultureIgnoreCase));
}
}
}
using System.Linq;
using Engine.Factories;
using Engine.Models;
using Engine.Services;
using Newtonsoft.Json;
namespace Engine.ViewModels
{
public class GameSession : BaseNotificationClass
{
private readonly MessageBroker _messageBroker = MessageBroker.GetInstance();
#region Properties
private GameDetails _gameDetails;
private Player _currentPlayer;
private Location _currentLocation;
private Battle _currentBattle;
private Monster _currentMonster;
private Trader _currentTrader;
[JsonIgnore]
public GameDetails GameDetails
{
get => _gameDetails;
set
{
_gameDetails = value;
OnPropertyChanged();
}
}
[JsonIgnore]
public World CurrentWorld { get; }
public Player CurrentPlayer
{
get => _currentPlayer;
set
{
if(_currentPlayer != null)
{
_currentPlayer.OnLeveledUp -= OnCurrentPlayerLeveledUp;
_currentPlayer.OnKilled -= OnPlayerKilled;
}
_currentPlayer = value;
if(_currentPlayer != null)
{
_currentPlayer.OnLeveledUp += OnCurrentPlayerLeveledUp;
_currentPlayer.OnKilled += OnPlayerKilled;
}
}
}
public Location CurrentLocation
{
get => _currentLocation;
set
{
_currentLocation = value;
OnPropertyChanged();
OnPropertyChanged(nameof(HasLocationToNorth));
OnPropertyChanged(nameof(HasLocationToEast));
OnPropertyChanged(nameof(HasLocationToWest));
OnPropertyChanged(nameof(HasLocationToSouth));
CompleteQuestsAtLocation();
GivePlayerQuestsAtLocation();
CurrentMonster = CurrentLocation.GetMonster();
CurrentTrader = CurrentLocation.TraderHere;
}
}
[JsonIgnore]
public Monster CurrentMonster
{
get => _currentMonster;
set
{
if(_currentBattle != null)
{
_currentBattle.OnCombatVictory -= OnCurrentMonsterKilled;
_currentBattle.Dispose();
_currentBattle = null;
}
_currentMonster = value;
if(_currentMonster != null)
{
_currentBattle = new Battle(CurrentPlayer, CurrentMonster);
_currentBattle.OnCombatVictory += OnCurrentMonsterKilled;
}
OnPropertyChanged();
OnPropertyChanged(nameof(HasMonster));
}
}
[JsonIgnore]
public Trader CurrentTrader
{
get => _currentTrader;
set
{
_currentTrader = value;
OnPropertyChanged();
OnPropertyChanged(nameof(HasTrader));
}
}
[JsonIgnore]
public bool HasLocationToNorth =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate, CurrentLocation.YCoordinate + 1) != null;
[JsonIgnore]
public bool HasLocationToEast =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate + 1, CurrentLocation.YCoordinate) != null;
[JsonIgnore]
public bool HasLocationToSouth =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate, CurrentLocation.YCoordinate - 1) != null;
[JsonIgnore]
public bool HasLocationToWest =>
CurrentWorld.LocationAt(CurrentLocation.XCoordinate - 1, CurrentLocation.YCoordinate) != null;
[JsonIgnore]
public bool HasMonster => CurrentMonster != null;
[JsonIgnore]
public bool HasTrader => CurrentTrader != null;
#endregion
public GameSession(Player player, int xCoordinate, int yCoordinate)
{
PopulateGameDetails();
CurrentWorld = WorldFactory.CreateWorld();
CurrentPlayer = player;
CurrentLocation = CurrentWorld.LocationAt(xCoordinate, yCoordinate);
}
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 PopulateGameDetails()
{
GameDetails = GameDetailsService.ReadGameDetails();
}
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.Inventory.HasAllTheseItems(quest.ItemsToComplete))
{
CurrentPlayer.RemoveItemsFromInventory(quest.ItemsToComplete);
_messageBroker.RaiseMessage("");
_messageBroker.RaiseMessage($"You completed the '{quest.Name}' quest");
// Give the player the quest rewards
_messageBroker.RaiseMessage($"You receive {quest.RewardExperiencePoints} experience points");
CurrentPlayer.AddExperience(quest.RewardExperiencePoints);
_messageBroker.RaiseMessage($"You receive {quest.RewardGold} gold");
CurrentPlayer.ReceiveGold(quest.RewardGold);
foreach(ItemQuantity itemQuantity in quest.RewardItems)
{
GameItem rewardItem = ItemFactory.CreateGameItem(itemQuantity.ItemID);
_messageBroker.RaiseMessage($"You receive a {rewardItem.Name}");
CurrentPlayer.AddItemToInventory(rewardItem);
}
// 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));
_messageBroker.RaiseMessage("");
_messageBroker.RaiseMessage($"You receive the '{quest.Name}' quest");
_messageBroker.RaiseMessage(quest.Description);
_messageBroker.RaiseMessage("Return with:");
foreach(ItemQuantity itemQuantity in quest.ItemsToComplete)
{
_messageBroker
.RaiseMessage($" {itemQuantity.Quantity} {ItemFactory.CreateGameItem(itemQuantity.ItemID).Name}");
}
_messageBroker.RaiseMessage("And you will receive:");
_messageBroker.RaiseMessage($" {quest.RewardExperiencePoints} experience points");
_messageBroker.RaiseMessage($" {quest.RewardGold} gold");
foreach(ItemQuantity itemQuantity in quest.RewardItems)
{
_messageBroker
.RaiseMessage($" {itemQuantity.Quantity} {ItemFactory.CreateGameItem(itemQuantity.ItemID).Name}");
}
}
}
}
public void AttackCurrentMonster()
{
_currentBattle?.AttackOpponent();
}
public void UseCurrentConsumable()
{
if(CurrentPlayer.CurrentConsumable != null)
{
if (_currentBattle == null)
{
CurrentPlayer.OnActionPerformed += OnConsumableActionPerformed;
}
CurrentPlayer.UseCurrentConsumable();
if (_currentBattle == null)
{
CurrentPlayer.OnActionPerformed -= OnConsumableActionPerformed;
}
}
}
private void OnConsumableActionPerformed(object sender, string result)
{
_messageBroker.RaiseMessage(result);
}
public void CraftItemUsing(Recipe recipe)
{
if(CurrentPlayer.Inventory.HasAllTheseItems(recipe.Ingredients))
{
CurrentPlayer.RemoveItemsFromInventory(recipe.Ingredients);
foreach(ItemQuantity itemQuantity in recipe.OutputItems)
{
for(int i = 0; i < itemQuantity.Quantity; i++)
{
GameItem outputItem = ItemFactory.CreateGameItem(itemQuantity.ItemID);
CurrentPlayer.AddItemToInventory(outputItem);
_messageBroker.RaiseMessage($"You craft 1 {outputItem.Name}");
}
}
}
else
{
_messageBroker.RaiseMessage("You do not have the required ingredients:");
foreach(ItemQuantity itemQuantity in recipe.Ingredients)
{
_messageBroker
.RaiseMessage($" {itemQuantity.Quantity} {ItemFactory.ItemName(itemQuantity.ItemID)}");
}
}
}
private void OnPlayerKilled(object sender, System.EventArgs e)
{
_messageBroker.RaiseMessage("");
_messageBroker.RaiseMessage("You have been killed.");
CurrentLocation = CurrentWorld.LocationAt(0, -1);
CurrentPlayer.CompletelyHeal();
}
private void OnCurrentMonsterKilled(object sender, System.EventArgs eventArgs)
{
// Get another monster to fight
CurrentMonster = CurrentLocation.GetMonster();
}
private void OnCurrentPlayerLeveledUp(object sender, System.EventArgs eventArgs)
{
_messageBroker.RaiseMessage($"You are now level {CurrentPlayer.Level}!");
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Engine.Services;
using Newtonsoft.Json;
namespace Engine.Models
{
public abstract class LivingEntity : BaseNotificationClass
{
#region Properties
private string _name;
private int _currentHitPoints;
private int _maximumHitPoints;
private int _gold;
private int _level;
private GameItem _currentWeapon;
private GameItem _currentConsumable;
private Inventory _inventory;
public ObservableCollection<PlayerAttribute> Attributes { get; } =
new ObservableCollection<PlayerAttribute>();
public string Name
{
get => _name;
private set
{
_name = value;
OnPropertyChanged();
}
}
public int CurrentHitPoints
{
get => _currentHitPoints;
private set
{
_currentHitPoints = value;
OnPropertyChanged();
}
}
public int MaximumHitPoints
{
get => _maximumHitPoints;
protected set
{
_maximumHitPoints = value;
OnPropertyChanged();
}
}
public int Gold
{
get => _gold;
private set
{
_gold = value;
OnPropertyChanged();
}
}
public int Level
{
get => _level;
protected set
{
_level = value;
OnPropertyChanged();
}
}
public Inventory Inventory
{
get => _inventory;
private set
{
_inventory = value;
OnPropertyChanged();
}
}
public GameItem CurrentWeapon
{
get => _currentWeapon;
set
{
if (_currentWeapon != null)
{
_currentWeapon.Action.OnActionPerformed -= RaiseActionPerformedEvent;
}
_currentWeapon = value;
if (_currentWeapon != null)
{
_currentWeapon.Action.OnActionPerformed += RaiseActionPerformedEvent;
}
OnPropertyChanged();
}
}
public GameItem CurrentConsumable
{
get => _currentConsumable;
set
{
if(_currentConsumable != null)
{
_currentConsumable.Action.OnActionPerformed -= RaiseActionPerformedEvent;
}
_currentConsumable = value;
if (_currentConsumable != null)
{
_currentConsumable.Action.OnActionPerformed += RaiseActionPerformedEvent;
}
OnPropertyChanged();
}
}
[JsonIgnore]
public bool IsAlive => CurrentHitPoints > 0;
[JsonIgnore]
public bool IsDead => !IsAlive;
#endregion
public event EventHandler<string> OnActionPerformed;
public event EventHandler OnKilled;
protected LivingEntity(string name, int maximumHitPoints, int currentHitPoints,
IEnumerable<PlayerAttribute> attributes, int gold, int level = 1)
{
Name = name;
MaximumHitPoints = maximumHitPoints;
CurrentHitPoints = currentHitPoints;
Gold = gold;
Level = level;
foreach (PlayerAttribute attribute in attributes)
{
Attributes.Add(attribute);
}
Inventory = new Inventory();
}
public void UseCurrentWeaponOn(LivingEntity target)
{
CurrentWeapon.PerformAction(this, target);
}
public void UseCurrentConsumable()
{
CurrentConsumable.PerformAction(this, this);
RemoveItemFromInventory(CurrentConsumable);
}
public void TakeDamage(int hitPointsOfDamage)
{
CurrentHitPoints -= hitPointsOfDamage;
if(IsDead)
{
CurrentHitPoints = 0;
RaiseOnKilledEvent();
}
}
public void Heal(int hitPointsToHeal)
{
CurrentHitPoints += hitPointsToHeal;
if(CurrentHitPoints > MaximumHitPoints)
{
CurrentHitPoints = MaximumHitPoints;
}
}
public void CompletelyHeal()
{
CurrentHitPoints = MaximumHitPoints;
}
public void ReceiveGold(int amountOfGold)
{
Gold += amountOfGold;
}
public void SpendGold(int amountOfGold)
{
if(amountOfGold > Gold)
{
throw new ArgumentOutOfRangeException($"{Name} only has {Gold} gold, and cannot spend {amountOfGold} gold");
}
Gold -= amountOfGold;
}
public void AddItemToInventory(GameItem item)
{
Inventory = Inventory.AddItem(item);
}
public void RemoveItemFromInventory(GameItem item)
{
Inventory = Inventory.RemoveItem(item);
}
public void RemoveItemsFromInventory(IEnumerable<ItemQuantity> itemQuantities)
{
Inventory = Inventory.RemoveItems(itemQuantities);
}
#region Private functions
private void RaiseOnKilledEvent()
{
OnKilled?.Invoke(this, new System.EventArgs());
}
private void RaiseActionPerformedEvent(object sender, string result)
{
OnActionPerformed?.Invoke(this, result);
}
#endregion
}
}
<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="{Binding GameDetails.Title}" Height="768" Width="1024"
KeyDown="MainWindow_OnKeyDown"
Closing="MainWindow_OnClosing">
<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 -->
<Menu Grid.Row="0" Grid.Column="0"
Grid.ColumnSpan="2"
FontSize="11pt"
Background="AliceBlue">
<MenuItem Header="File">
<MenuItem Header="Start New Game"
Click="StartNewGame_OnClick"
IsEnabled="False"/>
<Separator/>
<MenuItem Header="Load Game"
Click="LoadGame_OnClick"
IsEnabled="False"/>
<MenuItem Header="Save Game"
Click="SaveGame_OnClick"
IsEnabled="False"/>
<Separator/>
<MenuItem Header="Exit"
Click="Exit_OnClick"/>
</MenuItem>
<MenuItem Header="Help">
<MenuItem Header="Help"
IsEnabled="False"/>
<Separator/>
<MenuItem Header="About"
IsEnabled="False"/>
</MenuItem>
</Menu>
<!-- 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"/>
</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="Hit points:"/>
<Label Grid.Row="1" Grid.Column="1" Content="{Binding CurrentPlayer.CurrentHitPoints}"/>
<Label Grid.Row="2" Grid.Column="0" Content="Gold:"/>
<Label Grid.Row="2" Grid.Column="1" Content="{Binding CurrentPlayer.Gold}"/>
<Label Grid.Row="3" Grid.Column="0" Content="XP:"/>
<Label Grid.Row="3" Grid.Column="1" Content="{Binding CurrentPlayer.ExperiencePoints}"/>
<Label Grid.Row="4" Grid.Column="0" Content="Level:"/>
<Label Grid.Row="4" Grid.Column="1" Content="{Binding CurrentPlayer.Level}"/>
<!-- Player Attributes -->
<ListBox Grid.Row="5" Grid.Column="0"
Grid.ColumnSpan="2"
Background="Aquamarine"
BorderThickness="0"
Grid.IsSharedSizeScope="True"
ItemsSource="{Binding CurrentPlayer.Attributes}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Description"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DisplayName}"
HorizontalAlignment="Left"
MinWidth="100"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="ModifiedValue"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ModifiedValue}"
HorizontalAlignment="Right"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</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,
Converter={StaticResource FileToBitmapConverter}}"/>
<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,
Converter={StaticResource FileToBitmapConverter}}"/>
<StackPanel Grid.Row="2"
Visibility="{Binding HasMonster, Converter={StaticResource BooleanToVisibility}}"
HorizontalAlignment="Center"
Orientation="Horizontal">
<TextBlock>Current Hit Points:</TextBlock>
<TextBlock Text="{Binding CurrentMonster.CurrentHitPoints}" />
</StackPanel>
</Grid>
</Border>
</Grid>
<!-- Inventory, Quests, and Recipes -->
<Grid Grid.Row="2" Grid.Column="0"
Background="BurlyWood">
<TabControl x:Name="PlayerDataTabControl">
<TabItem Header="Inventory"
x:Name="InventoryTabItem">
<DataGrid ItemsSource="{Binding CurrentPlayer.Inventory.GroupedInventory}"
AutoGenerateColumns="False"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="Description"
Binding="{Binding Item.Name, Mode=OneWay}"
Width="*"/>
<DataGridTextColumn Header="Qty"
IsReadOnly="True"
Width="Auto"
Binding="{Binding Quantity, Mode=OneWay}"/>
<DataGridTextColumn Header="Price"
Binding="{Binding Item.Price, Mode=OneWay}"
Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Quests"
x:Name="QuestsTabItem">
<DataGrid ItemsSource="{Binding CurrentPlayer.Quests}"
AutoGenerateColumns="False"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding PlayerQuest.Name, Mode=OneWay}"
Width="*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ToolTip"
Value="{Binding PlayerQuest.ToolTipContents}"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Done?"
Binding="{Binding IsCompleted, Mode=OneWay}"
Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Recipes"
x:Name="RecipesTabItem">
<DataGrid ItemsSource="{Binding CurrentPlayer.Recipes}"
AutoGenerateColumns="False"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="Name"
Binding="{Binding Name, Mode=OneWay}"
Width="*">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="ToolTip"
Value="{Binding ToolTipContents}"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTemplateColumn MinWidth="75">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="OnClick_Craft"
Width="55"
Content="Craft"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</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"
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"
Visibility="{Binding HasMonster, Converter={StaticResource BooleanToVisibility}}"
ItemsSource="{Binding CurrentPlayer.Inventory.Weapons}"
SelectedItem="{Binding CurrentPlayer.CurrentWeapon}"
DisplayMemberPath="Name"/>
<Button Grid.Row="0" Grid.Column="2"
Visibility="{Binding HasMonster, Converter={StaticResource BooleanToVisibility}}"
Content="Use"
Click="OnClick_AttackMonster"/>
<ComboBox Grid.Row="1" Grid.Column="0"
Visibility="{Binding CurrentPlayer.Inventory.HasConsumable, Converter={StaticResource BooleanToVisibility}}"
ItemsSource="{Binding CurrentPlayer.Inventory.Consumables}"
SelectedItem="{Binding CurrentPlayer.CurrentConsumable}"
DisplayMemberPath="Name"/>
<Button Grid.Row="1" Grid.Column="2"
Visibility="{Binding CurrentPlayer.Inventory.HasConsumable, Converter={StaticResource BooleanToVisibility}}"
Content="Use"
Click="OnClick_UseCurrentConsumable"/>
</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"
Click="OnClick_DisplayTradeScreen"
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>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using Engine.EventArgs;
using Engine.Models;
using Engine.Services;
using Engine.ViewModels;
using Microsoft.Win32;
using WPFUI.Windows;
namespace WPFUI
{
public partial class MainWindow : Window
{
private const string SAVE_GAME_FILE_EXTENSION = "soscsrpg";
private readonly MessageBroker _messageBroker = MessageBroker.GetInstance();
private readonly Dictionary<Key, Action> _userInputActions =
new Dictionary<Key, Action>();
private GameSession _gameSession;
public MainWindow(Player player)
{
InitializeComponent();
InitializeUserInputActions();
SetActiveGameSessionTo(new GameSession(player, 0, 0));
}
private void OnClick_MoveNorth(object sender, RoutedEventArgs e)
{
_gameSession.MoveNorth();
}
private void OnClick_MoveWest(object sender, RoutedEventArgs e)
{
_gameSession.MoveWest();
}
private void OnClick_MoveEast(object sender, RoutedEventArgs e)
{
_gameSession.MoveEast();
}
private void OnClick_MoveSouth(object sender, RoutedEventArgs e)
{
_gameSession.MoveSouth();
}
private void OnClick_AttackMonster(object sender, RoutedEventArgs e)
{
_gameSession.AttackCurrentMonster();
}
private void OnClick_UseCurrentConsumable(object sender, RoutedEventArgs e)
{
_gameSession.UseCurrentConsumable();
}
private void OnGameMessageRaised(object sender, GameMessageEventArgs e)
{
GameMessages.Document.Blocks.Add(new Paragraph(new Run(e.Message)));
GameMessages.ScrollToEnd();
}
private void OnClick_DisplayTradeScreen(object sender, RoutedEventArgs e)
{
if(_gameSession.CurrentTrader != null)
{
TradeScreen tradeScreen = new TradeScreen();
tradeScreen.Owner = this;
tradeScreen.DataContext = _gameSession;
tradeScreen.ShowDialog();
}
}
private void OnClick_Craft(object sender, RoutedEventArgs e)
{
Recipe recipe = ((FrameworkElement)sender).DataContext as Recipe;
_gameSession.CraftItemUsing(recipe);
}
private void InitializeUserInputActions()
{
_userInputActions.Add(Key.W, () => _gameSession.MoveNorth());
_userInputActions.Add(Key.A, () => _gameSession.MoveWest());
_userInputActions.Add(Key.S, () => _gameSession.MoveSouth());
_userInputActions.Add(Key.D, () => _gameSession.MoveEast());
_userInputActions.Add(Key.Z, () => _gameSession.AttackCurrentMonster());
_userInputActions.Add(Key.C, () => _gameSession.UseCurrentConsumable());
_userInputActions.Add(Key.I, () => SetTabFocusTo("InventoryTabItem"));
_userInputActions.Add(Key.Q, () => SetTabFocusTo("QuestsTabItem"));
_userInputActions.Add(Key.R, () => SetTabFocusTo("RecipesTabItem"));
_userInputActions.Add(Key.T, () => OnClick_DisplayTradeScreen(this, new RoutedEventArgs()));
}
private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
{
if(_userInputActions.ContainsKey(e.Key))
{
_userInputActions[e.Key].Invoke();
}
}
private void SetTabFocusTo(string tabName)
{
foreach(object item in PlayerDataTabControl.Items)
{
if (item is TabItem tabItem)
{
if (tabItem.Name == tabName)
{
tabItem.IsSelected = true;
return;
}
}
}
}
private void SetActiveGameSessionTo(GameSession gameSession)
{
// Unsubscribe from OnMessageRaised, or we will get double messages
_messageBroker.OnMessageRaised -= OnGameMessageRaised;
_gameSession = gameSession;
DataContext = _gameSession;
// Clear out previous game's messages
GameMessages.Document.Blocks.Clear();
_messageBroker.OnMessageRaised += OnGameMessageRaised;
}
private void StartNewGame_OnClick(object sender, RoutedEventArgs e)
{
//SetActiveGameSessionTo(new GameSession());
}
private void LoadGame_OnClick(object sender, RoutedEventArgs e)
{
//OpenFileDialog openFileDialog =
// new OpenFileDialog
// {
// InitialDirectory = AppDomain.CurrentDomain.BaseDirectory,
// Filter = $"Saved games (*.{SAVE_GAME_FILE_EXTENSION})|*.{SAVE_GAME_FILE_EXTENSION}"
// };
//if(openFileDialog.ShowDialog() == true)
//{
// SetActiveGameSessionTo(SaveGameService.LoadLastSaveOrCreateNew(openFileDialog.FileName));
//}
}
private void SaveGame_OnClick(object sender, RoutedEventArgs e)
{
SaveGame();
}
private void Exit_OnClick(object sender, RoutedEventArgs e)
{
Close();
}
private void MainWindow_OnClosing(object sender, CancelEventArgs e)
{
YesNoWindow message =
new YesNoWindow("Save Game", "Do you want to save your game?");
message.Owner = GetWindow(this);
message.ShowDialog();
if (message.ClickedYes)
{
SaveGame();
}
}
private void SaveGame()
{
SaveFileDialog saveFileDialog =
new SaveFileDialog
{
InitialDirectory = AppDomain.CurrentDomain.BaseDirectory,
Filter = $"Saved games (*.{SAVE_GAME_FILE_EXTENSION})|*.{SAVE_GAME_FILE_EXTENSION}"
};
if (saveFileDialog.ShowDialog() == true)
{
//SaveGameService.Save(_gameSession, saveFileDialog.FileName);
}
}
}
}
using System.Collections.Generic;
using Engine.Factories;
using Engine.Services;
namespace Engine.Models
{
public class Monster : LivingEntity
{
private readonly List<ItemPercentage> _lootTable =
new List<ItemPercentage>();
public int ID { get; }
public string ImageName { get; }
public int RewardExperiencePoints { get; }
public Monster(int id, string name, string imageName,
int maximumHitPoints, IEnumerable<PlayerAttribute> attributes,
GameItem currentWeapon,
int rewardExperiencePoints, int gold) :
base(name, maximumHitPoints, maximumHitPoints, attributes, gold)
{
ID = id;
ImageName = imageName;
CurrentWeapon = currentWeapon;
RewardExperiencePoints = rewardExperiencePoints;
}
public void AddItemToLootTable(int id, int percentage)
{
// Remove the entry from the loot table,
// if it already contains an entry with this ID
_lootTable.RemoveAll(ip => ip.ID == id);
_lootTable.Add(new ItemPercentage(id, percentage));
}
public Monster GetNewInstance()
{
// "Clone" this monster to a new Monster object
Monster newMonster =
new Monster(ID, Name, ImageName, MaximumHitPoints, Attributes,
CurrentWeapon, RewardExperiencePoints, Gold);
foreach(ItemPercentage itemPercentage in _lootTable)
{
// Clone the loot table - even though we probably won't need it
newMonster.AddItemToLootTable(itemPercentage.ID, itemPercentage.Percentage);
// Populate the new monster's inventory, using the loot table
if(DiceService.Instance.Roll(100).Value <= itemPercentage.Percentage)
{
newMonster.AddItemToInventory(ItemFactory.CreateGameItem(itemPercentage.ID));
}
}
return newMonster;
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using Engine.Models;
using Engine.Services;
using Engine.Shared;
namespace Engine.Factories
{
public static class MonsterFactory
{
private const string GAME_DATA_FILENAME = ".\\GameData\\Monsters.xml";
private static readonly GameDetails s_gameDetails;
private static readonly List<Monster> _baseMonsters = new List<Monster>();
static MonsterFactory()
{
if(File.Exists(GAME_DATA_FILENAME))
{
s_gameDetails = GameDetailsService.ReadGameDetails();
XmlDocument data = new XmlDocument();
data.LoadXml(File.ReadAllText(GAME_DATA_FILENAME));
string rootImagePath =
data.SelectSingleNode("/Monsters")
.AttributeAsString("RootImagePath");
LoadMonstersFromNodes(data.SelectNodes("/Monsters/Monster"), rootImagePath);
}
else
{
throw new FileNotFoundException($"Missing data file: {GAME_DATA_FILENAME}");
}
}
private static void LoadMonstersFromNodes(XmlNodeList nodes, string rootImagePath)
{
if(nodes == null)
{
return;
}
foreach(XmlNode node in nodes)
{
var attributes = s_gameDetails.PlayerAttributes;
attributes.First(a => a.Key.Equals("DEX")).BaseValue =
Convert.ToInt32(node.SelectSingleNode("./Dexterity").InnerText);
attributes.First(a => a.Key.Equals("DEX")).ModifiedValue =
Convert.ToInt32(node.SelectSingleNode("./Dexterity").InnerText);
Monster monster =
new Monster(node.AttributeAsInt("ID"),
node.AttributeAsString("Name"),
$".{rootImagePath}{node.AttributeAsString("ImageName")}",
node.AttributeAsInt("MaximumHitPoints"),
attributes,
ItemFactory.CreateGameItem(node.AttributeAsInt("WeaponID")),
node.AttributeAsInt("RewardXP"),
node.AttributeAsInt("Gold"));
XmlNodeList lootItemNodes = node.SelectNodes("./LootItems/LootItem");
if(lootItemNodes != null)
{
foreach(XmlNode lootItemNode in lootItemNodes)
{
monster.AddItemToLootTable(lootItemNode.AttributeAsInt("ID"),
lootItemNode.AttributeAsInt("Percentage"));
}
}
_baseMonsters.Add(monster);
}
}
public static Monster GetMonster(int id)
{
return _baseMonsters.FirstOrDefault(m => m.ID == id)?.GetNewInstance();
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace Engine.Models
{
public class Player : LivingEntity
{
#region Properties
private int _experiencePoints;
public int ExperiencePoints
{
get => _experiencePoints;
private set
{
_experiencePoints = value;
OnPropertyChanged();
SetLevelAndMaximumHitPoints();
}
}
public ObservableCollection<QuestStatus> Quests { get; } =
new ObservableCollection<QuestStatus>();
public ObservableCollection<Recipe> Recipes { get; } =
new ObservableCollection<Recipe>();
#endregion
public event EventHandler OnLeveledUp;
public Player(string name, int experiencePoints,
int maximumHitPoints, int currentHitPoints,
IEnumerable<PlayerAttribute> attributes, int gold) :
base(name, maximumHitPoints, currentHitPoints, attributes, gold)
{
ExperiencePoints = experiencePoints;
}
public void AddExperience(int experiencePoints)
{
ExperiencePoints += experiencePoints;
}
public void LearnRecipe(Recipe recipe)
{
if(!Recipes.Any(r => r.ID == recipe.ID))
{
Recipes.Add(recipe);
}
}
private void SetLevelAndMaximumHitPoints()
{
int originalLevel = Level;
Level = (ExperiencePoints / 100) + 1;
if (Level != originalLevel)
{
MaximumHitPoints = Level * 10;
OnLeveledUp?.Invoke(this, System.EventArgs.Empty);
}
}
}
}
using System;
using System.IO;
using Engine.Factories;
using Engine.Models;
using Engine.ViewModels;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Engine.Services
{
public static class SaveGameService
{
public static void Save(GameSession gameSession, string fileName)
{
File.WriteAllText(fileName,
JsonConvert.SerializeObject(gameSession, Formatting.Indented));
}
public static GameSession LoadLastSaveOrCreateNew(string fileName)
{
if(!File.Exists(fileName))
{
return null;
}
// Save game file exists, so create the GameSession object from it.
JObject data = JObject.Parse(File.ReadAllText(fileName));
// Populate Player object
Player player = CreatePlayer(data);
int x = (int)data[nameof(GameSession.CurrentLocation)][nameof(Location.XCoordinate)];
int y = (int)data[nameof(GameSession.CurrentLocation)][nameof(Location.YCoordinate)];
// Create GameSession object with saved game data
return new GameSession(player, x, y);
}
private static Player CreatePlayer(JObject data)
{
//string fileVersion = FileVersion(data);
//Player player;
//switch(fileVersion)
//{
// case "0.1.000":
// player =
// new Player((string)data[nameof(GameSession.CurrentPlayer)][nameof(Player.Name)],
// (int)data[nameof(GameSession.CurrentPlayer)][nameof(Player.ExperiencePoints)],
// (int)data[nameof(GameSession.CurrentPlayer)][nameof(Player.MaximumHitPoints)],
// (int)data[nameof(GameSession.CurrentPlayer)][nameof(Player.CurrentHitPoints)],
// (int)data[nameof(GameSession.CurrentPlayer)][nameof(Player.Dexterity)],
// (int)data[nameof(GameSession.CurrentPlayer)][nameof(Player.Gold)]);
// break;
// default:
// throw new InvalidDataException($"File version '{fileVersion}' not recognized");
//}
//PopulatePlayerInventory(data, player);
//PopulatePlayerQuests(data, player);
//PopulatePlayerRecipes(data, player);
//return player;
return null;
}
private static void PopulatePlayerInventory(JObject data, Player player)
{
string fileVersion = FileVersion(data);
switch(fileVersion)
{
case "0.1.000":
foreach(JToken itemToken in (JArray)data[nameof(GameSession.CurrentPlayer)]
[nameof(Player.Inventory)]
[nameof(Inventory.Items)])
{
int itemId = (int)itemToken[nameof(GameItem.ItemTypeID)];
player.AddItemToInventory(ItemFactory.CreateGameItem(itemId));
}
break;
default:
throw new InvalidDataException($"File version '{fileVersion}' not recognized");
}
}
private static void PopulatePlayerQuests(JObject data, Player player)
{
string fileVersion = FileVersion(data);
switch(fileVersion)
{
case "0.1.000":
foreach(JToken questToken in (JArray)data[nameof(GameSession.CurrentPlayer)]
[nameof(Player.Quests)])
{
int questId =
(int)questToken[nameof(QuestStatus.PlayerQuest)][nameof(QuestStatus.PlayerQuest.ID)];
Quest quest = QuestFactory.GetQuestByID(questId);
QuestStatus questStatus = new QuestStatus(quest);
questStatus.IsCompleted = (bool)questToken[nameof(QuestStatus.IsCompleted)];
player.Quests.Add(questStatus);
}
break;
default:
throw new InvalidDataException($"File version '{fileVersion}' not recognized");
}
}
private static void PopulatePlayerRecipes(JObject data, Player player)
{
string fileVersion = FileVersion(data);
switch(fileVersion)
{
case "0.1.000":
foreach(JToken recipeToken in
(JArray)data[nameof(GameSession.CurrentPlayer)][nameof(Player.Recipes)])
{
int recipeId = (int)recipeToken[nameof(Recipe.ID)];
Recipe recipe = RecipeFactory.RecipeByID(recipeId);
player.Recipes.Add(recipe);
}
break;
default:
throw new InvalidDataException($"File version '{fileVersion}' not recognized");
}
}
private static string FileVersion(JObject data)
{
return (string)data[nameof(GameSession.GameDetails.Version)];
}
}
}
using Engine.Models;
using Engine.Services;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestEngine.Services
{
[TestClass]
public class TestCombatService
{
[TestMethod]
public void Test_FirstAttacker()
{
// Player and monster with dexterity 12
//Player player = new Player("", 0, 0, 0, 18, 0);
//Monster monster = new Monster(0, "", "", 0, 12, null, 0, 0);
//CombatService.Combatant result = CombatService.FirstAttacker(player, monster);
}
}
}
using Engine.Models;
using Engine.ViewModels;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestEngine.ViewModels
{
[TestClass]
public class TestGameSession
{
[TestMethod]
public void TestCreateGameSession()
{
//Player player = new Player("", 0, 10, 10, 15, 10);
//GameSession gameSession = new GameSession(player, 0, 0);
//Assert.IsNotNull(gameSession.CurrentPlayer);
//Assert.AreEqual("Town Square", gameSession.CurrentLocation.Name);
}
[TestMethod]
public void TestPlayerMovesHomeAndIsCompletelyHealedOnKilled()
{
//Player player = new Player("", 0, 10, 10, 15, 10);
//GameSession gameSession = new GameSession(player, 0, 0);
//gameSession.CurrentPlayer.TakeDamage(999);
//Assert.AreEqual("Home", gameSession.CurrentLocation.Name);
//Assert.AreEqual(gameSession.CurrentPlayer.Level * 10, gameSession.CurrentPlayer.CurrentHitPoints);
}
}
}
using System.Linq;
using Engine.Services;
using Engine.ViewModels;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestEngine.Services
{
[TestClass]
public class TestSaveGameService
{
[TestMethod]
public void Test_Restore_0_1_000()
{
//GameSession gameSession =
// SaveGameService
// .LoadLastSaveOrCreateNew(@".\TestFiles\SavedGames\Game_0_1_000.soscsrpg");
//// Game session data
//Assert.AreEqual("0.1.000", gameSession.GameDetails.Version);
//Assert.AreEqual(0, gameSession.CurrentLocation.XCoordinate);
//Assert.AreEqual(1, gameSession.CurrentLocation.YCoordinate);
//// Player data
//Assert.AreEqual("Scott", gameSession.CurrentPlayer.Name);
//Assert.AreEqual(18, gameSession.CurrentPlayer.Dexterity);
//Assert.AreEqual(8, gameSession.CurrentPlayer.CurrentHitPoints);
//Assert.AreEqual(10, gameSession.CurrentPlayer.MaximumHitPoints);
//Assert.AreEqual(20, gameSession.CurrentPlayer.ExperiencePoints);
//Assert.AreEqual(1, gameSession.CurrentPlayer.Level);
//Assert.AreEqual(1000000, gameSession.CurrentPlayer.Gold);
//// Player quest data
//Assert.AreEqual(1, gameSession.CurrentPlayer.Quests.Count);
//Assert.AreEqual(1, gameSession.CurrentPlayer.Quests[0].PlayerQuest.ID);
//Assert.IsFalse(gameSession.CurrentPlayer.Quests[0].IsCompleted);
//// Player recipe data
//Assert.AreEqual(1, gameSession.CurrentPlayer.Recipes.Count);
//Assert.AreEqual(1, gameSession.CurrentPlayer.Recipes[0].ID);
//// Player inventory data
//Assert.AreEqual(5, gameSession.CurrentPlayer.Inventory.Items.Count);
//Assert.AreEqual(1, gameSession.CurrentPlayer.Inventory.Items.Count(i => i.ItemTypeID.Equals(1001)));
//Assert.AreEqual(1, gameSession.CurrentPlayer.Inventory.Items.Count(i => i.ItemTypeID.Equals(2001)));
//Assert.AreEqual(1, gameSession.CurrentPlayer.Inventory.Items.Count(i => i.ItemTypeID.Equals(3001)));
//Assert.AreEqual(1, gameSession.CurrentPlayer.Inventory.Items.Count(i => i.ItemTypeID.Equals(3002)));
//Assert.AreEqual(1, gameSession.CurrentPlayer.Inventory.Items.Count(i => i.ItemTypeID.Equals(3003)));
}
}
}
using System.Collections.Generic;
namespace Engine.Models
{
public class Trader : LivingEntity
{
public int ID { get; }
public Trader(int id, string name) : base(name, 9999, 9999, new List<PlayerAttribute>(), 9999)
{
ID = id;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment