Lesson 24.1 - Make the SuperAdventure source code easier to understand and modify
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
namespace Engine | |
{ | |
public class InventoryItem : INotifyPropertyChanged | |
{ | |
private Item _details; | |
private int _quantity; | |
public Item Details | |
{ | |
get { return _details; } | |
set | |
{ | |
_details = value; | |
OnPropertyChanged("Details"); | |
} | |
} | |
public int Quantity | |
{ | |
get { return _quantity; } | |
set | |
{ | |
_quantity = value; | |
OnPropertyChanged("Quantity"); | |
OnPropertyChanged("Description"); | |
} | |
} | |
public int ItemID | |
{ | |
get { return Details.ID; } | |
} | |
public string Description | |
{ | |
get { return Quantity > 1 ? Details.NamePlural : Details.Name; } | |
} | |
public int Price | |
{ | |
get { return Details.Price; } | |
} | |
public InventoryItem(Item details, int quantity) | |
{ | |
Details = details; | |
Quantity = quantity; | |
} | |
public event PropertyChangedEventHandler PropertyChanged; | |
protected void OnPropertyChanged(string name) | |
{ | |
if(PropertyChanged != null) | |
{ | |
PropertyChanged(this, new PropertyChangedEventArgs(name)); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
namespace Engine | |
{ | |
public class LivingCreature : INotifyPropertyChanged | |
{ | |
private int _currentHitPoints; | |
public int CurrentHitPoints | |
{ | |
get { return _currentHitPoints; } | |
set | |
{ | |
_currentHitPoints = value; | |
OnPropertyChanged("CurrentHitPoints"); | |
} | |
} | |
public int MaximumHitPoints { get; set; } | |
public bool IsDead { get { return CurrentHitPoints <= 0; } } | |
public LivingCreature(int currentHitPoints, int maximumHitPoints) | |
{ | |
CurrentHitPoints = currentHitPoints; | |
MaximumHitPoints = maximumHitPoints; | |
} | |
public event PropertyChangedEventHandler PropertyChanged; | |
protected void OnPropertyChanged(string name) | |
{ | |
if(PropertyChanged != null) | |
{ | |
PropertyChanged(this, new PropertyChangedEventArgs(name)); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Engine | |
{ | |
public class Location | |
{ | |
public int ID { get; set; } | |
public string Name { get; set; } | |
public string Description { get; set; } | |
public Item ItemRequiredToEnter { get; set; } | |
public Quest QuestAvailableHere { get; set; } | |
public Monster MonsterLivingHere { get; set; } | |
public Vendor VendorWorkingHere { get; set; } | |
public Location LocationToNorth { get; set; } | |
public Location LocationToEast { get; set; } | |
public Location LocationToSouth { get; set; } | |
public Location LocationToWest { get; set; } | |
public bool HasAQuest { get { return QuestAvailableHere != null; } } | |
public bool DoesNotHaveAnItemRequiredToEnter { get { return ItemRequiredToEnter == null; } } | |
public Location(int id, string name, string description, | |
Item itemRequiredToEnter = null, Quest questAvailableHere = null, Monster monsterLivingHere = null) | |
{ | |
ID = id; | |
Name = name; | |
Description = description; | |
ItemRequiredToEnter = itemRequiredToEnter; | |
QuestAvailableHere = questAvailableHere; | |
MonsterLivingHere = monsterLivingHere; | |
} | |
public Monster NewInstanceOfMonsterLivingHere() | |
{ | |
return MonsterLivingHere == null ? null : MonsterLivingHere.NewInstanceOfMonster(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Linq; | |
namespace Engine | |
{ | |
public class Monster : LivingCreature | |
{ | |
public int ID { get; set; } | |
public string Name { get; set; } | |
public int MaximumDamage { get; set; } | |
public int RewardExperiencePoints { get; set; } | |
public int RewardGold { get; set; } | |
// All possible items (with percentages) that this type of monster could have | |
public List<LootItem> LootTable { get; set; } | |
// The items this instance of the monster has in their inventory | |
internal List<InventoryItem> LootItems { get; } | |
public Monster(int id, string name, int maximumDamage, int rewardExperiencePoints, int rewardGold, int currentHitPoints, int maximumHitPoints) | |
: base(currentHitPoints, maximumHitPoints) | |
{ | |
ID = id; | |
Name = name; | |
MaximumDamage = maximumDamage; | |
RewardExperiencePoints = rewardExperiencePoints; | |
RewardGold = rewardGold; | |
LootTable = new List<LootItem>(); | |
LootItems = new List<InventoryItem>(); | |
} | |
internal Monster NewInstanceOfMonster() | |
{ | |
Monster newMonster = | |
new Monster(ID, Name, MaximumDamage, RewardExperiencePoints, RewardGold, CurrentHitPoints, MaximumHitPoints); | |
// Add items to the lootedItems list, comparing a random number to the drop percentage | |
foreach(LootItem lootItem in LootTable.Where(lootItem => RandomNumberGenerator.NumberBetween(1, 100) <= lootItem.DropPercentage)) | |
{ | |
newMonster.LootItems.Add(new InventoryItem(lootItem.Details, 1)); | |
} | |
// If no items were randomly selected, add the default loot item(s). | |
if(newMonster.LootItems.Count == 0) | |
{ | |
foreach(LootItem lootItem in LootTable.Where(x => x.IsDefaultItem)) | |
{ | |
newMonster.LootItems.Add(new InventoryItem(lootItem.Details, 1)); | |
} | |
} | |
return newMonster; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Xml; | |
namespace Engine | |
{ | |
public class Player : LivingCreature | |
{ | |
private int _gold; | |
private int _experiencePoints; | |
private Location _currentLocation; | |
public event EventHandler<MessageEventArgs> OnMessage; | |
public int Gold | |
{ | |
get { return _gold; } | |
set | |
{ | |
_gold = value; | |
OnPropertyChanged("Gold"); | |
} | |
} | |
public int ExperiencePoints | |
{ | |
get { return _experiencePoints; } | |
private set | |
{ | |
_experiencePoints = value; | |
OnPropertyChanged("ExperiencePoints"); | |
OnPropertyChanged("Level"); | |
} | |
} | |
public int Level | |
{ | |
get { return ((ExperiencePoints / 100) + 1); } | |
} | |
public Location CurrentLocation | |
{ | |
get { return _currentLocation; } | |
set | |
{ | |
_currentLocation = value; | |
OnPropertyChanged("CurrentLocation"); | |
} | |
} | |
public Weapon CurrentWeapon { get; set; } | |
public BindingList<InventoryItem> Inventory { get; set; } | |
public List<Weapon> Weapons | |
{ | |
get { return Inventory.Where(x => x.Details is Weapon).Select(x => x.Details as Weapon).ToList(); } | |
} | |
public List<HealingPotion> Potions | |
{ | |
get { return Inventory.Where(x => x.Details is HealingPotion).Select(x => x.Details as HealingPotion).ToList(); } | |
} | |
public BindingList<PlayerQuest> Quests { get; set; } | |
private Monster CurrentMonster { get; set; } | |
private Player(int currentHitPoints, int maximumHitPoints, int gold, int experiencePoints) : base(currentHitPoints, maximumHitPoints) | |
{ | |
Gold = gold; | |
ExperiencePoints = experiencePoints; | |
Inventory = new BindingList<InventoryItem>(); | |
Quests = new BindingList<PlayerQuest>(); | |
} | |
public static Player CreateDefaultPlayer() | |
{ | |
Player player = new Player(10, 10, 20, 0); | |
player.Inventory.Add(new InventoryItem(World.ItemByID(World.ITEM_ID_RUSTY_SWORD), 1)); | |
player.CurrentLocation = World.LocationByID(World.LOCATION_ID_HOME); | |
return player; | |
} | |
public static Player CreatePlayerFromXmlString(string xmlPlayerData) | |
{ | |
try | |
{ | |
XmlDocument playerData = new XmlDocument(); | |
playerData.LoadXml(xmlPlayerData); | |
int currentHitPoints = Convert.ToInt32(playerData.SelectSingleNode("/Player/Stats/CurrentHitPoints").InnerText); | |
int maximumHitPoints = Convert.ToInt32(playerData.SelectSingleNode("/Player/Stats/MaximumHitPoints").InnerText); | |
int gold = Convert.ToInt32(playerData.SelectSingleNode("/Player/Stats/Gold").InnerText); | |
int experiencePoints = Convert.ToInt32(playerData.SelectSingleNode("/Player/Stats/ExperiencePoints").InnerText); | |
Player player = new Player(currentHitPoints, maximumHitPoints, gold, experiencePoints); | |
int currentLocationID = Convert.ToInt32(playerData.SelectSingleNode("/Player/Stats/CurrentLocation").InnerText); | |
player.CurrentLocation = World.LocationByID(currentLocationID); | |
if(playerData.SelectSingleNode("/Player/Stats/CurrentWeapon") != null) | |
{ | |
int currentWeaponID = Convert.ToInt32(playerData.SelectSingleNode("/Player/Stats/CurrentWeapon").InnerText); | |
player.CurrentWeapon = (Weapon)World.ItemByID(currentWeaponID); | |
} | |
foreach(XmlNode node in playerData.SelectNodes("/Player/InventoryItems/InventoryItem")) | |
{ | |
int id = Convert.ToInt32(node.Attributes["ID"].Value); | |
int quantity = Convert.ToInt32(node.Attributes["Quantity"].Value); | |
for(int i = 0; i < quantity; i++) | |
{ | |
player.AddItemToInventory(World.ItemByID(id)); | |
} | |
} | |
foreach(XmlNode node in playerData.SelectNodes("/Player/PlayerQuests/PlayerQuest")) | |
{ | |
int id = Convert.ToInt32(node.Attributes["ID"].Value); | |
bool isCompleted = Convert.ToBoolean(node.Attributes["IsCompleted"].Value); | |
PlayerQuest playerQuest = new PlayerQuest(World.QuestByID(id)); | |
playerQuest.IsCompleted = isCompleted; | |
player.Quests.Add(playerQuest); | |
} | |
return player; | |
} | |
catch | |
{ | |
// If there was an error with the XML data, return a default player object | |
return CreateDefaultPlayer(); | |
} | |
} | |
public static Player CreatePlayerFromDatabase(int currentHitPoints, int maximumHitPoints, int gold, int experiencePoints, int currentLocationID) | |
{ | |
Player player = new Player(currentHitPoints, maximumHitPoints, gold, experiencePoints); | |
player.MoveTo(World.LocationByID(currentLocationID)); | |
return player; | |
} | |
public void MoveTo(Location location) | |
{ | |
if(PlayerDoesNotHaveTheRequiredItemToEnter(location)) | |
{ | |
RaiseMessage("You must have a " + location.ItemRequiredToEnter.Name + " to enter this location."); | |
return; | |
} | |
// The player can enter this location | |
CurrentLocation = location; | |
CompletelyHeal(); | |
if(location.HasAQuest) | |
{ | |
if(PlayerDoesNotHaveThisQuest(location.QuestAvailableHere)) | |
{ | |
GiveQuestToPlayer(location.QuestAvailableHere); | |
} | |
else | |
{ | |
if(PlayerHasNotCompleted(location.QuestAvailableHere) && | |
PlayerHasAllQuestCompletionItemsFor(location.QuestAvailableHere)) | |
{ | |
GivePlayerQuestRewards(location.QuestAvailableHere); | |
} | |
} | |
} | |
SetTheCurrentMonsterForTheCurrentLocation(location); | |
} | |
public void MoveNorth() | |
{ | |
if(CurrentLocation.LocationToNorth != null) | |
{ | |
MoveTo(CurrentLocation.LocationToNorth); | |
} | |
} | |
public void MoveEast() | |
{ | |
if(CurrentLocation.LocationToEast != null) | |
{ | |
MoveTo(CurrentLocation.LocationToEast); | |
} | |
} | |
public void MoveSouth() | |
{ | |
if(CurrentLocation.LocationToSouth != null) | |
{ | |
MoveTo(CurrentLocation.LocationToSouth); | |
} | |
} | |
public void MoveWest() | |
{ | |
if(CurrentLocation.LocationToWest != null) | |
{ | |
MoveTo(CurrentLocation.LocationToWest); | |
} | |
} | |
public void UseWeapon(Weapon weapon) | |
{ | |
int damage = RandomNumberGenerator.NumberBetween(weapon.MinimumDamage, weapon.MaximumDamage); | |
if(damage == 0) | |
{ | |
RaiseMessage("You missed the " + CurrentMonster.Name); | |
} | |
else | |
{ | |
CurrentMonster.CurrentHitPoints -= damage; | |
RaiseMessage("You hit the " + CurrentMonster.Name + " for " + damage + " points."); | |
} | |
if(CurrentMonster.IsDead) | |
{ | |
LootTheCurrentMonster(); | |
// "Move" to the current location, to refresh the current monster | |
MoveTo(CurrentLocation); | |
} | |
else | |
{ | |
LetTheMonsterAttack(); | |
} | |
} | |
private void LootTheCurrentMonster() | |
{ | |
RaiseMessage(""); | |
RaiseMessage("You defeated the " + CurrentMonster.Name); | |
RaiseMessage("You receive " + CurrentMonster.RewardExperiencePoints + " experience points"); | |
RaiseMessage("You receive " + CurrentMonster.RewardGold + " gold"); | |
AddExperiencePoints(CurrentMonster.RewardExperiencePoints); | |
Gold += CurrentMonster.RewardGold; | |
// Give monster's loot items to the player | |
foreach(InventoryItem inventoryItem in CurrentMonster.LootItems) | |
{ | |
AddItemToInventory(inventoryItem.Details); | |
RaiseMessage(string.Format("You loot {0} {1}", inventoryItem.Quantity, inventoryItem.Description)); | |
} | |
RaiseMessage(""); | |
} | |
public void UsePotion(HealingPotion potion) | |
{ | |
RaiseMessage("You drink a " + potion.Name); | |
HealPlayer(potion.AmountToHeal); | |
RemoveItemFromInventory(potion); | |
// The player used their turn to drink the potion, so let the monster attack now | |
LetTheMonsterAttack(); | |
} | |
public void AddItemToInventory(Item itemToAdd, int quantity = 1) | |
{ | |
InventoryItem existingItemInInventory = Inventory.SingleOrDefault(ii => ii.Details.ID == itemToAdd.ID); | |
if(existingItemInInventory == null) | |
{ | |
Inventory.Add(new InventoryItem(itemToAdd, quantity)); | |
} | |
else | |
{ | |
existingItemInInventory.Quantity += quantity; | |
} | |
RaiseInventoryChangedEvent(itemToAdd); | |
} | |
public void RemoveItemFromInventory(Item itemToRemove, int quantity = 1) | |
{ | |
InventoryItem item = Inventory.SingleOrDefault(ii => ii.Details.ID == itemToRemove.ID && ii.Quantity >= quantity); | |
if(item != null) | |
{ | |
item.Quantity -= quantity; | |
if(item.Quantity == 0) | |
{ | |
Inventory.Remove(item); | |
} | |
RaiseInventoryChangedEvent(itemToRemove); | |
} | |
} | |
public string ToXmlString() | |
{ | |
XmlDocument playerData = new XmlDocument(); | |
// Create the top-level XML node | |
XmlNode player = playerData.CreateElement("Player"); | |
playerData.AppendChild(player); | |
// Create the "Stats" child node to hold the other player statistics nodes | |
XmlNode stats = playerData.CreateElement("Stats"); | |
player.AppendChild(stats); | |
// Create the child nodes for the "Stats" node | |
CreateNewChildXmlNode(playerData, stats, "CurrentHitPoints", CurrentHitPoints); | |
CreateNewChildXmlNode(playerData, stats, "MaximumHitPoints", MaximumHitPoints); | |
CreateNewChildXmlNode(playerData, stats, "Gold", Gold); | |
CreateNewChildXmlNode(playerData, stats, "ExperiencePoints", ExperiencePoints); | |
CreateNewChildXmlNode(playerData, stats, "CurrentLocation", CurrentLocation.ID); | |
if(CurrentWeapon != null) | |
{ | |
CreateNewChildXmlNode(playerData, stats, "CurrentWeapon", CurrentWeapon.ID); | |
} | |
// Create the "InventoryItems" child node to hold each InventoryItem node | |
XmlNode inventoryItems = playerData.CreateElement("InventoryItems"); | |
player.AppendChild(inventoryItems); | |
// Create an "InventoryItem" node for each item in the player's inventory | |
foreach(InventoryItem item in Inventory) | |
{ | |
XmlNode inventoryItem = playerData.CreateElement("InventoryItem"); | |
AddXmlAttributeToNode(playerData, inventoryItem, "ID", item.Details.ID); | |
AddXmlAttributeToNode(playerData, inventoryItem, "Quantity", item.Quantity); | |
inventoryItems.AppendChild(inventoryItem); | |
} | |
// Create the "PlayerQuests" child node to hold each PlayerQuest node | |
XmlNode playerQuests = playerData.CreateElement("PlayerQuests"); | |
player.AppendChild(playerQuests); | |
// Create a "PlayerQuest" node for each quest the player has acquired | |
foreach(PlayerQuest quest in Quests) | |
{ | |
XmlNode playerQuest = playerData.CreateElement("PlayerQuest"); | |
AddXmlAttributeToNode(playerData, playerQuest, "ID", quest.Details.ID); | |
AddXmlAttributeToNode(playerData, playerQuest, "IsCompleted", quest.IsCompleted); | |
playerQuests.AppendChild(playerQuest); | |
} | |
return playerData.InnerXml; // The XML document, as a string, so we can save the data to disk | |
} | |
private bool HasRequiredItemToEnterThisLocation(Location location) | |
{ | |
if(location.DoesNotHaveAnItemRequiredToEnter) | |
{ | |
return true; | |
} | |
// See if the player has the required item in their inventory | |
return Inventory.Any(ii => ii.Details.ID == location.ItemRequiredToEnter.ID); | |
} | |
private void SetTheCurrentMonsterForTheCurrentLocation(Location location) | |
{ | |
// Populate the current monster with this location's monster (or null, if there is no monster here) | |
CurrentMonster = location.NewInstanceOfMonsterLivingHere(); | |
if(CurrentMonster != null) | |
{ | |
RaiseMessage("You see a " + location.MonsterLivingHere.Name); | |
} | |
} | |
private bool PlayerDoesNotHaveTheRequiredItemToEnter(Location location) | |
{ | |
return !HasRequiredItemToEnterThisLocation(location); | |
} | |
private bool PlayerDoesNotHaveThisQuest(Quest quest) | |
{ | |
return Quests.All(pq => pq.Details.ID != quest.ID); | |
} | |
private bool PlayerHasNotCompleted(Quest quest) | |
{ | |
return Quests.Any(pq => pq.Details.ID == quest.ID && !pq.IsCompleted); | |
} | |
private void GiveQuestToPlayer(Quest quest) | |
{ | |
RaiseMessage("You receive the " + quest.Name + " quest."); | |
RaiseMessage(quest.Description); | |
RaiseMessage("To complete it, return with:"); | |
foreach(QuestCompletionItem qci in quest.QuestCompletionItems) | |
{ | |
RaiseMessage(string.Format("{0} {1}", qci.Quantity, | |
qci.Quantity == 1 ? qci.Details.Name : qci.Details.NamePlural)); | |
} | |
RaiseMessage(""); | |
Quests.Add(new PlayerQuest(quest)); | |
} | |
private bool PlayerHasAllQuestCompletionItemsFor(Quest quest) | |
{ | |
// See if the player has all the items needed to complete the quest here | |
foreach(QuestCompletionItem qci in quest.QuestCompletionItems) | |
{ | |
// Check each item in the player's inventory, to see if they have it, and enough of it | |
if(!Inventory.Any(ii => ii.Details.ID == qci.Details.ID && ii.Quantity >= qci.Quantity)) | |
{ | |
return false; | |
} | |
} | |
// If we got here, then the player must have all the required items, and enough of them, to complete the quest. | |
return true; | |
} | |
private void RemoveQuestCompletionItems(Quest quest) | |
{ | |
foreach(QuestCompletionItem qci in quest.QuestCompletionItems) | |
{ | |
InventoryItem item = Inventory.SingleOrDefault(ii => ii.Details.ID == qci.Details.ID); | |
if(item != null) | |
{ | |
RemoveItemFromInventory(item.Details, qci.Quantity); | |
} | |
} | |
} | |
private void AddExperiencePoints(int experiencePointsToAdd) | |
{ | |
ExperiencePoints += experiencePointsToAdd; | |
MaximumHitPoints = (Level * 10); | |
} | |
private void GivePlayerQuestRewards(Quest quest) | |
{ | |
RaiseMessage(""); | |
RaiseMessage("You complete the '" + quest.Name + "' quest."); | |
RaiseMessage("You receive: "); | |
RaiseMessage(quest.RewardExperiencePoints + " experience points"); | |
RaiseMessage(quest.RewardGold + " gold"); | |
RaiseMessage(quest.RewardItem.Name, true); | |
AddExperiencePoints(quest.RewardExperiencePoints); | |
Gold += quest.RewardGold; | |
RemoveQuestCompletionItems(quest); | |
AddItemToInventory(quest.RewardItem); | |
MarkPlayerQuestCompleted(quest); | |
} | |
private void MarkPlayerQuestCompleted(Quest quest) | |
{ | |
PlayerQuest playerQuest = Quests.SingleOrDefault(pq => pq.Details.ID == quest.ID); | |
if(playerQuest != null) | |
{ | |
playerQuest.IsCompleted = true; | |
} | |
} | |
private void LetTheMonsterAttack() | |
{ | |
int damageToPlayer = RandomNumberGenerator.NumberBetween(0, CurrentMonster.MaximumDamage); | |
RaiseMessage("The " + CurrentMonster.Name + " did " + damageToPlayer + " points of damage."); | |
CurrentHitPoints -= damageToPlayer; | |
if(IsDead) | |
{ | |
RaiseMessage("The " + CurrentMonster.Name + " killed you."); | |
MoveHome(); | |
} | |
} | |
private void HealPlayer(int hitPointsToHeal) | |
{ | |
CurrentHitPoints = Math.Min(CurrentHitPoints + hitPointsToHeal, MaximumHitPoints); | |
} | |
private void CompletelyHeal() | |
{ | |
CurrentHitPoints = MaximumHitPoints; | |
} | |
private void MoveHome() | |
{ | |
MoveTo(World.LocationByID(World.LOCATION_ID_HOME)); | |
} | |
private void CreateNewChildXmlNode(XmlDocument document, XmlNode parentNode, string elementName, object value) | |
{ | |
XmlNode node = document.CreateElement(elementName); | |
node.AppendChild(document.CreateTextNode(value.ToString())); | |
parentNode.AppendChild(node); | |
} | |
private void AddXmlAttributeToNode(XmlDocument document, XmlNode node, string attributeName, object value) | |
{ | |
XmlAttribute attribute = document.CreateAttribute(attributeName); | |
attribute.Value = value.ToString(); | |
node.Attributes.Append(attribute); | |
} | |
private void RaiseInventoryChangedEvent(Item item) | |
{ | |
if(item is Weapon) | |
{ | |
OnPropertyChanged("Weapons"); | |
} | |
if(item is HealingPotion) | |
{ | |
OnPropertyChanged("Potions"); | |
} | |
} | |
private void RaiseMessage(string message, bool addExtraNewLine = false) | |
{ | |
if(OnMessage != null) | |
{ | |
OnMessage(this, new MessageEventArgs(message, addExtraNewLine)); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
namespace Engine | |
{ | |
public class PlayerQuest : INotifyPropertyChanged | |
{ | |
private Quest _details; | |
private bool _isCompleted; | |
public Quest Details | |
{ | |
get { return _details; } | |
set | |
{ | |
_details = value; | |
OnPropertyChanged("Details"); | |
} | |
} | |
public bool IsCompleted | |
{ | |
get { return _isCompleted; } | |
set | |
{ | |
_isCompleted = value; | |
OnPropertyChanged("IsCompleted"); | |
OnPropertyChanged("Name"); | |
} | |
} | |
public string Name | |
{ | |
get { return Details.Name; } | |
} | |
public PlayerQuest(Quest details) | |
{ | |
Details = details; | |
IsCompleted = false; | |
} | |
public event PropertyChangedEventHandler PropertyChanged; | |
protected void OnPropertyChanged(string name) | |
{ | |
if(PropertyChanged != null) | |
{ | |
PropertyChanged(this, new PropertyChangedEventArgs(name)); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.ComponentModel; | |
using System.IO; | |
using System.Linq; | |
using Engine; | |
namespace SuperAdventureConsole | |
{ | |
public class Program | |
{ | |
private const string PLAYER_DATA_FILE_NAME = "PlayerData.xml"; | |
private static Player _player; | |
private static void Main(string[] args) | |
{ | |
// Load the player | |
LoadGameData(); | |
Console.WriteLine("Type 'Help' to see a list of commands"); | |
Console.WriteLine(""); | |
DisplayCurrentLocation(); | |
// Connect player events to functions that will display in the UI | |
_player.PropertyChanged += Player_OnPropertyChanged; | |
_player.OnMessage += Player_OnMessage; | |
// Infinite loop, until the user types "exit" | |
while(true) | |
{ | |
// Display a prompt, so the user knows to type something | |
Console.Write(">"); | |
// Wait for the user to type something, and press the <Enter> key | |
string userInput = Console.ReadLine(); | |
// If they typed a blank line, loop back and wait for input again | |
if(userInput == null) | |
{ | |
continue; | |
} | |
// Convert to lower-case, to make comparisons easier | |
string cleanedInput = userInput.ToLower(); | |
// Save the current game data, and break out of the "while(true)" loop | |
if(cleanedInput == "exit") | |
{ | |
SaveGameData(); | |
break; | |
} | |
// If the user typed something, try to determine what to do | |
ParseInput(cleanedInput); | |
} | |
} | |
private static void Player_OnPropertyChanged(object sender, PropertyChangedEventArgs e) | |
{ | |
if(e.PropertyName == "CurrentLocation") | |
{ | |
DisplayCurrentLocation(); | |
if(_player.CurrentLocation.VendorWorkingHere != null) | |
{ | |
Console.WriteLine("You see a vendor here: {0}", _player.CurrentLocation.VendorWorkingHere.Name); | |
} | |
} | |
} | |
private static void Player_OnMessage(object sender, MessageEventArgs e) | |
{ | |
Console.WriteLine(e.Message); | |
if(e.AddExtraNewLine) | |
{ | |
Console.WriteLine(""); | |
} | |
} | |
private static void ParseInput(string input) | |
{ | |
if(input.Contains("help") || input == "?") | |
{ | |
DisplayHelpText(); | |
} | |
else if(input == "stats") | |
{ | |
DisplayPlayerStats(); | |
} | |
else if(input == "look") | |
{ | |
DisplayCurrentLocation(); | |
} | |
else if(input.Contains("north")) | |
{ | |
if(_player.CurrentLocation.LocationToNorth == null) | |
{ | |
Console.WriteLine("You cannot move North"); | |
} | |
else | |
{ | |
_player.MoveNorth(); | |
} | |
} | |
else if(input.Contains("east")) | |
{ | |
if(_player.CurrentLocation.LocationToEast == null) | |
{ | |
Console.WriteLine("You cannot move East"); | |
} | |
else | |
{ | |
_player.MoveEast(); | |
} | |
} | |
else if(input.Contains("south")) | |
{ | |
if(_player.CurrentLocation.LocationToSouth == null) | |
{ | |
Console.WriteLine("You cannot move South"); | |
} | |
else | |
{ | |
_player.MoveSouth(); | |
} | |
} | |
else if(input.Contains("west")) | |
{ | |
if(_player.CurrentLocation.LocationToWest == null) | |
{ | |
Console.WriteLine("You cannot move West"); | |
} | |
else | |
{ | |
_player.MoveWest(); | |
} | |
} | |
else if(input == "inventory") | |
{ | |
foreach(InventoryItem inventoryItem in _player.Inventory) | |
{ | |
Console.WriteLine("{0}: {1}", inventoryItem.Description, inventoryItem.Quantity); | |
} | |
} | |
else if(input == "quests") | |
{ | |
if(_player.Quests.Count == 0) | |
{ | |
Console.WriteLine("You do not have any quests"); | |
} | |
else | |
{ | |
foreach(PlayerQuest playerQuest in _player.Quests) | |
{ | |
Console.WriteLine("{0}: {1}", playerQuest.Name, | |
playerQuest.IsCompleted ? "Completed" : "Incomplete"); | |
} | |
} | |
} | |
else if(input.Contains("attack")) | |
{ | |
AttackMonster(); | |
} | |
else if(input.StartsWith("equip ")) | |
{ | |
EquipWeapon(input); | |
} | |
else if(input.StartsWith("drink ")) | |
{ | |
DrinkPotion(input); | |
} | |
else if(input == "trade") | |
{ | |
ViewTradeInventory(); | |
} | |
else if(input.StartsWith("buy ")) | |
{ | |
BuyItem(input); | |
} | |
else if(input.StartsWith("sell ")) | |
{ | |
SellItem(input); | |
} | |
else | |
{ | |
Console.WriteLine("I do not understand"); | |
Console.WriteLine("Type 'Help' to see a list of available commands"); | |
} | |
// Write a blank line, to keep the UI a little cleaner | |
Console.WriteLine(""); | |
} | |
private static void DisplayHelpText() | |
{ | |
Console.WriteLine("Available commands"); | |
Console.WriteLine("===================================="); | |
Console.WriteLine("Stats - Display player information"); | |
Console.WriteLine("Look - Get the description of your location"); | |
Console.WriteLine("Inventory - Display your inventory"); | |
Console.WriteLine("Quests - Display your quests"); | |
Console.WriteLine("Attack - Fight the monster"); | |
Console.WriteLine("Equip <weapon name> - Set your current weapon"); | |
Console.WriteLine("Drink <potion name> - Drink a potion"); | |
Console.WriteLine("Trade - display your inventory and vendor's inventory"); | |
Console.WriteLine("Buy <item name> - Buy an item from a vendor"); | |
Console.WriteLine("Sell <item name> - Sell an item to a vendor"); | |
Console.WriteLine("North - Move North"); | |
Console.WriteLine("South - Move South"); | |
Console.WriteLine("East - Move East"); | |
Console.WriteLine("West - Move West"); | |
Console.WriteLine("Exit - Save the game and exit"); | |
} | |
private static void DisplayPlayerStats() | |
{ | |
Console.WriteLine("Current hit points: {0}", _player.CurrentHitPoints); | |
Console.WriteLine("Maximum hit points: {0}", _player.MaximumHitPoints); | |
Console.WriteLine("Experience Points: {0}", _player.ExperiencePoints); | |
Console.WriteLine("Level: {0}", _player.Level); | |
Console.WriteLine("Gold: {0}", _player.Gold); | |
} | |
private static void AttackMonster() | |
{ | |
if(_player.CurrentLocation.MonsterLivingHere == null) | |
{ | |
Console.WriteLine("There is nothing here to attack"); | |
} | |
else | |
{ | |
if(_player.CurrentWeapon == null) | |
{ | |
// Select the first weapon in the player's inventory | |
// (or 'null', if they do not have any weapons) | |
_player.CurrentWeapon = _player.Weapons.FirstOrDefault(); | |
} | |
if(_player.CurrentWeapon == null) | |
{ | |
Console.WriteLine("You do not have any weapons"); | |
} | |
else | |
{ | |
_player.UseWeapon(_player.CurrentWeapon); | |
} | |
} | |
} | |
private static void EquipWeapon(string input) | |
{ | |
string inputWeaponName = input.Substring(6).Trim(); | |
if(string.IsNullOrEmpty(inputWeaponName)) | |
{ | |
Console.WriteLine("You must enter the name of the weapon to equip"); | |
} | |
else | |
{ | |
Weapon weaponToEquip = | |
_player.Weapons.SingleOrDefault( | |
x => x.Name.ToLower() == inputWeaponName || x.NamePlural.ToLower() == inputWeaponName); | |
if(weaponToEquip == null) | |
{ | |
Console.WriteLine("You do not have the weapon: {0}", inputWeaponName); | |
} | |
else | |
{ | |
_player.CurrentWeapon = weaponToEquip; | |
Console.WriteLine("You equip your {0}", _player.CurrentWeapon.Name); | |
} | |
} | |
} | |
private static void DrinkPotion(string input) | |
{ | |
string inputPotionName = input.Substring(6).Trim(); | |
if(string.IsNullOrEmpty(inputPotionName)) | |
{ | |
Console.WriteLine("You must enter the name of the potion to drink"); | |
} | |
else | |
{ | |
HealingPotion potionToDrink = | |
_player.Potions.SingleOrDefault( | |
x => x.Name.ToLower() == inputPotionName || x.NamePlural.ToLower() == inputPotionName); | |
if(potionToDrink == null) | |
{ | |
Console.WriteLine("You do not have the potion: {0}", inputPotionName); | |
} | |
else | |
{ | |
_player.UsePotion(potionToDrink); | |
} | |
} | |
} | |
private static void ViewTradeInventory() | |
{ | |
if(LocationDoesNotHaveVendor()) | |
{ | |
return; | |
} | |
Console.WriteLine("PLAYER INVENTORY"); | |
Console.WriteLine("================"); | |
if(_player.Inventory.Count(x => x.Price != World.UNSELLABLE_ITEM_PRICE) == 0) | |
{ | |
Console.WriteLine("You do not have any inventory"); | |
} | |
else | |
{ | |
foreach( | |
InventoryItem inventoryItem in _player.Inventory.Where(x => x.Price != World.UNSELLABLE_ITEM_PRICE)) | |
{ | |
Console.WriteLine("{0} {1} Price: {2}", inventoryItem.Quantity, inventoryItem.Description, | |
inventoryItem.Price); | |
} | |
} | |
Console.WriteLine(""); | |
Console.WriteLine("VENDOR INVENTORY"); | |
Console.WriteLine("================"); | |
if(_player.CurrentLocation.VendorWorkingHere.Inventory.Count == 0) | |
{ | |
Console.WriteLine("The vendor does not have any inventory"); | |
} | |
else | |
{ | |
foreach(InventoryItem inventoryItem in _player.CurrentLocation.VendorWorkingHere.Inventory) | |
{ | |
Console.WriteLine("{0} {1} Price: {2}", inventoryItem.Quantity, inventoryItem.Description, | |
inventoryItem.Price); | |
} | |
} | |
} | |
private static void BuyItem(string input) | |
{ | |
if(LocationDoesNotHaveVendor()) | |
{ | |
return; | |
} | |
string itemName = input.Substring(4).Trim(); | |
if(string.IsNullOrEmpty(itemName)) | |
{ | |
Console.WriteLine("You must enter the name of the item to buy"); | |
} | |
else | |
{ | |
// Get the InventoryItem from the trader's inventory | |
InventoryItem itemToBuy = | |
_player.CurrentLocation.VendorWorkingHere.Inventory.SingleOrDefault( | |
x => x.Details.Name.ToLower() == itemName); | |
// Check if the vendor has the item | |
if(itemToBuy == null) | |
{ | |
Console.WriteLine("The vendor does not have any {0}", itemName); | |
} | |
else | |
{ | |
// Check if the player has enough gold to buy the item | |
if(_player.Gold < itemToBuy.Price) | |
{ | |
Console.WriteLine("You do not have enough gold to buy a {0}", itemToBuy.Description); | |
} | |
else | |
{ | |
// Success! Buy the item | |
_player.AddItemToInventory(itemToBuy.Details); | |
_player.Gold -= itemToBuy.Price; | |
Console.WriteLine("You bought one {0} for {1} gold", itemToBuy.Details.Name, itemToBuy.Price); | |
} | |
} | |
} | |
} | |
private static void SellItem(string input) | |
{ | |
if(LocationDoesNotHaveVendor()) | |
{ | |
return; | |
} | |
string itemName = input.Substring(5).Trim(); | |
if(string.IsNullOrEmpty(itemName)) | |
{ | |
Console.WriteLine("You must enter the name of the item to sell"); | |
} | |
else | |
{ | |
// Get the InventoryItem from the player's inventory | |
InventoryItem itemToSell = | |
_player.Inventory.SingleOrDefault(x => x.Details.Name.ToLower() == itemName && | |
x.Quantity > 0 && | |
x.Price != World.UNSELLABLE_ITEM_PRICE); | |
// Check if the player has the item entered | |
if(itemToSell == null) | |
{ | |
Console.WriteLine("The player cannot sell any {0}", itemName); | |
} | |
else | |
{ | |
// Sell the item | |
_player.RemoveItemFromInventory(itemToSell.Details); | |
_player.Gold += itemToSell.Price; | |
Console.WriteLine("You receive {0} gold for your {1}", itemToSell.Price, itemToSell.Details.Name); | |
} | |
} | |
} | |
private static bool LocationDoesNotHaveVendor() | |
{ | |
bool locationDoesNotHaveVendor = _player.CurrentLocation.VendorWorkingHere == null; | |
Console.WriteLine("There is no vendor at this location"); | |
return locationDoesNotHaveVendor; | |
} | |
private static void DisplayCurrentLocation() | |
{ | |
Console.WriteLine("You are at: {0}", _player.CurrentLocation.Name); | |
if(_player.CurrentLocation.Description != "") | |
{ | |
Console.WriteLine(_player.CurrentLocation.Description); | |
} | |
} | |
private static void LoadGameData() | |
{ | |
_player = PlayerDataMapper.CreateFromDatabase(); | |
if(_player == null) | |
{ | |
if(File.Exists(PLAYER_DATA_FILE_NAME)) | |
{ | |
_player = Player.CreatePlayerFromXmlString(File.ReadAllText(PLAYER_DATA_FILE_NAME)); | |
} | |
else | |
{ | |
_player = Player.CreateDefaultPlayer(); | |
} | |
} | |
} | |
private static void SaveGameData() | |
{ | |
File.WriteAllText(PLAYER_DATA_FILE_NAME, _player.ToXmlString()); | |
PlayerDataMapper.SaveToDatabase(_player); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
namespace Engine | |
{ | |
public class Quest | |
{ | |
public int ID { get; set; } | |
public string Name { get; set; } | |
public string Description { get; set; } | |
public int RewardExperiencePoints { get; set; } | |
public int RewardGold { get; set; } | |
public List<QuestCompletionItem> QuestCompletionItems { get; set; } | |
public Item RewardItem { get; set; } | |
public Quest(int id, string name, string description, int rewardExperiencePoints, int rewardGold) | |
{ | |
ID = id; | |
Name = name; | |
Description = description; | |
RewardExperiencePoints = rewardExperiencePoints; | |
RewardGold = rewardGold; | |
QuestCompletionItems = new List<QuestCompletionItem>(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Engine | |
{ | |
public class QuestCompletionItem | |
{ | |
public Item Details { get; set; } | |
public int Quantity { get; set; } | |
public QuestCompletionItem(Item details, int quantity) | |
{ | |
Details = details; | |
Quantity = quantity; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.ComponentModel; | |
using System.Linq; | |
using System.Windows.Forms; | |
using System.IO; | |
using Engine; | |
namespace SuperAdventure | |
{ | |
public partial class SuperAdventure : Form | |
{ | |
private const string PLAYER_DATA_FILE_NAME = "PlayerData.xml"; | |
private Player _player; | |
public SuperAdventure() | |
{ | |
InitializeComponent(); | |
_player = PlayerDataMapper.CreateFromDatabase(); | |
if(_player == null) | |
{ | |
if(File.Exists(PLAYER_DATA_FILE_NAME)) | |
{ | |
_player = Player.CreatePlayerFromXmlString(File.ReadAllText(PLAYER_DATA_FILE_NAME)); | |
} | |
else | |
{ | |
_player = Player.CreateDefaultPlayer(); | |
} | |
} | |
lblHitPoints.DataBindings.Add("Text", _player, "CurrentHitPoints"); | |
lblGold.DataBindings.Add("Text", _player, "Gold"); | |
lblExperience.DataBindings.Add("Text", _player, "ExperiencePoints"); | |
lblLevel.DataBindings.Add("Text", _player, "Level"); | |
dgvInventory.RowHeadersVisible = false; | |
dgvInventory.AutoGenerateColumns = false; | |
dgvInventory.DataSource = _player.Inventory; | |
dgvInventory.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Name", | |
Width = 197, | |
DataPropertyName = "Description" | |
}); | |
dgvInventory.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Quantity", | |
DataPropertyName = "Quantity" | |
}); | |
dgvQuests.RowHeadersVisible = false; | |
dgvQuests.AutoGenerateColumns = false; | |
dgvQuests.DataSource = _player.Quests; | |
dgvQuests.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Name", | |
Width = 197, | |
DataPropertyName = "Name" | |
}); | |
dgvQuests.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Done?", | |
DataPropertyName = "IsCompleted" | |
}); | |
cboWeapons.DataSource = _player.Weapons; | |
cboWeapons.DisplayMember = "Name"; | |
cboWeapons.ValueMember = "Id"; | |
if(_player.CurrentWeapon != null) | |
{ | |
cboWeapons.SelectedItem = _player.CurrentWeapon; | |
} | |
cboWeapons.SelectedIndexChanged += cboWeapons_SelectedIndexChanged; | |
cboPotions.DataSource = _player.Potions; | |
cboPotions.DisplayMember = "Name"; | |
cboPotions.ValueMember = "Id"; | |
_player.PropertyChanged += PlayerOnPropertyChanged; | |
_player.OnMessage += DisplayMessage; | |
_player.MoveTo(_player.CurrentLocation); | |
} | |
private void DisplayMessage(object sender, MessageEventArgs messageEventArgs) | |
{ | |
rtbMessages.Text += messageEventArgs.Message + Environment.NewLine; | |
if(messageEventArgs.AddExtraNewLine) | |
{ | |
rtbMessages.Text += Environment.NewLine; | |
} | |
rtbMessages.SelectionStart = rtbMessages.Text.Length; | |
rtbMessages.ScrollToCaret(); | |
} | |
private void PlayerOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs) | |
{ | |
if(propertyChangedEventArgs.PropertyName == "Weapons") | |
{ | |
cboWeapons.DataSource = _player.Weapons; | |
if(!_player.Weapons.Any()) | |
{ | |
cboWeapons.Visible = false; | |
btnUseWeapon.Visible = false; | |
} | |
} | |
if(propertyChangedEventArgs.PropertyName == "Potions") | |
{ | |
cboPotions.DataSource = _player.Potions; | |
if(!_player.Potions.Any()) | |
{ | |
cboPotions.Visible = false; | |
btnUsePotion.Visible = false; | |
} | |
} | |
if(propertyChangedEventArgs.PropertyName == "CurrentLocation") | |
{ | |
// Show/hide available movement buttons | |
btnNorth.Visible = (_player.CurrentLocation.LocationToNorth != null); | |
btnEast.Visible = (_player.CurrentLocation.LocationToEast != null); | |
btnSouth.Visible = (_player.CurrentLocation.LocationToSouth != null); | |
btnWest.Visible = (_player.CurrentLocation.LocationToWest != null); | |
btnTrade.Visible = (_player.CurrentLocation.VendorWorkingHere != null); | |
// Display current location name and description | |
rtbLocation.Text = _player.CurrentLocation.Name + Environment.NewLine; | |
rtbLocation.Text += _player.CurrentLocation.Description + Environment.NewLine; | |
if(_player.CurrentLocation.MonsterLivingHere == null) | |
{ | |
cboWeapons.Visible = false; | |
cboPotions.Visible = false; | |
btnUseWeapon.Visible = false; | |
btnUsePotion.Visible = false; | |
} | |
else | |
{ | |
cboWeapons.Visible = _player.Weapons.Any(); | |
cboPotions.Visible = _player.Potions.Any(); | |
btnUseWeapon.Visible = _player.Weapons.Any(); | |
btnUsePotion.Visible = _player.Potions.Any(); | |
} | |
} | |
} | |
private void btnNorth_Click(object sender, EventArgs e) | |
{ | |
_player.MoveNorth(); | |
} | |
private void btnEast_Click(object sender, EventArgs e) | |
{ | |
_player.MoveEast(); | |
} | |
private void btnSouth_Click(object sender, EventArgs e) | |
{ | |
_player.MoveSouth(); | |
} | |
private void btnWest_Click(object sender, EventArgs e) | |
{ | |
_player.MoveWest(); | |
} | |
private void btnUseWeapon_Click(object sender, EventArgs e) | |
{ | |
// Get the currently selected weapon from the cboWeapons ComboBox | |
Weapon currentWeapon = (Weapon)cboWeapons.SelectedItem; | |
_player.UseWeapon(currentWeapon); | |
} | |
private void btnUsePotion_Click(object sender, EventArgs e) | |
{ | |
// Get the currently selected potion from the combobox | |
HealingPotion potion = (HealingPotion)cboPotions.SelectedItem; | |
_player.UsePotion(potion); | |
} | |
private void SuperAdventure_FormClosing(object sender, FormClosingEventArgs e) | |
{ | |
File.WriteAllText(PLAYER_DATA_FILE_NAME, _player.ToXmlString()); | |
PlayerDataMapper.SaveToDatabase(_player); | |
} | |
private void cboWeapons_SelectedIndexChanged(object sender, EventArgs e) | |
{ | |
_player.CurrentWeapon = (Weapon)cboWeapons.SelectedItem; | |
} | |
private void btnTrade_Click(object sender, EventArgs e) | |
{ | |
TradingScreen tradingScreen = new TradingScreen(_player); | |
tradingScreen.StartPosition = FormStartPosition.CenterParent; | |
tradingScreen.ShowDialog(this); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Windows.Forms; | |
using Engine; | |
namespace SuperAdventure | |
{ | |
public partial class TradingScreen : Form | |
{ | |
private Player _currentPlayer; | |
// Commented out this property, because I chose to pass the player as a parameter in the constructor. | |
//public Player CurrentPlayer { get; set; } | |
public TradingScreen(Player player) | |
{ | |
_currentPlayer = player; | |
InitializeComponent(); | |
// Style, to display numeric column values | |
DataGridViewCellStyle rightAlignedCellStyle = new DataGridViewCellStyle(); | |
rightAlignedCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight; | |
// Populate the datagrid for the player's inventory | |
dgvMyItems.RowHeadersVisible = false; | |
dgvMyItems.AutoGenerateColumns = false; | |
// This hidden column holds the item ID, so we know which item to sell | |
dgvMyItems.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
DataPropertyName = "ItemID", | |
Visible = false | |
}); | |
dgvMyItems.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Name", | |
Width = 100, | |
DataPropertyName = "Description" | |
}); | |
dgvMyItems.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Qty", | |
Width = 30, | |
DefaultCellStyle = rightAlignedCellStyle, | |
DataPropertyName = "Quantity" | |
}); | |
dgvMyItems.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Price", | |
Width = 35, | |
DefaultCellStyle = rightAlignedCellStyle, | |
DataPropertyName = "Price" | |
}); | |
dgvMyItems.Columns.Add(new DataGridViewButtonColumn | |
{ | |
Text = "Sell 1", | |
UseColumnTextForButtonValue = true, | |
Width = 50, | |
DataPropertyName = "ItemID" | |
}); | |
// Bind the player's inventory to the datagridview | |
dgvMyItems.DataSource = _currentPlayer.Inventory; | |
// When the user clicks on a row, call this function | |
dgvMyItems.CellClick += dgvMyItems_CellClick; | |
// Populate the datagrid for the vendor's inventory | |
dgvVendorItems.RowHeadersVisible = false; | |
dgvVendorItems.AutoGenerateColumns = false; | |
// This hidden column holds the item ID, so we know which item to sell | |
dgvVendorItems.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
DataPropertyName = "ItemID", | |
Visible = false | |
}); | |
dgvVendorItems.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Name", | |
Width = 100, | |
DataPropertyName = "Description" | |
}); | |
dgvVendorItems.Columns.Add(new DataGridViewTextBoxColumn | |
{ | |
HeaderText = "Price", | |
Width = 35, | |
DefaultCellStyle = rightAlignedCellStyle, | |
DataPropertyName = "Price" | |
}); | |
dgvVendorItems.Columns.Add(new DataGridViewButtonColumn | |
{ | |
Text = "Buy 1", | |
UseColumnTextForButtonValue = true, | |
Width = 50, | |
DataPropertyName = "ItemID" | |
}); | |
// Bind the vendor's inventory to the datagridview | |
dgvVendorItems.DataSource = _currentPlayer.CurrentLocation.VendorWorkingHere.Inventory; | |
// When the user clicks on a row, call this function | |
dgvVendorItems.CellClick += dgvVendorItems_CellClick; | |
} | |
private void dgvMyItems_CellClick(object sender, DataGridViewCellEventArgs e) | |
{ | |
// The first column of a datagridview has a ColumnIndex = 0 | |
// This is known as a "zero-based" array/collection/list. | |
// You start counting with 0. | |
// | |
// The 5th column (ColumnIndex = 4) is the column with the button. | |
// So, if the player clicked the button column, we will sell an item from that row. | |
if(e.ColumnIndex == 4) | |
{ | |
// This gets the ID value of the item, from the hidden 1st column | |
// Remember, ColumnIndex = 0, for the first column | |
var itemID = dgvMyItems.Rows[e.RowIndex].Cells[0].Value; | |
// Get the Item object for the selected item row | |
Item itemBeingSold = World.ItemByID(Convert.ToInt32(itemID)); | |
if(itemBeingSold.Price == World.UNSELLABLE_ITEM_PRICE) | |
{ | |
MessageBox.Show("You cannot sell the " + itemBeingSold.Name); | |
} | |
else | |
{ | |
// Remove one of these items from the player's inventory | |
_currentPlayer.RemoveItemFromInventory(itemBeingSold); | |
// Give the player the gold for the item being sold. | |
_currentPlayer.Gold += itemBeingSold.Price; | |
} | |
} | |
} | |
private void dgvVendorItems_CellClick(object sender, DataGridViewCellEventArgs e) | |
{ | |
// The 4th column (ColumnIndex = 3) has the "Buy 1" button. | |
if(e.ColumnIndex == 3) | |
{ | |
// This gets the ID value of the item, from the hidden 1st column | |
var itemID = dgvVendorItems.Rows[e.RowIndex].Cells[0].Value; | |
// Get the Item object for the selected item row | |
Item itemBeingBought = World.ItemByID(Convert.ToInt32(itemID)); | |
// Check if the player has enough gold to buy the item | |
if(_currentPlayer.Gold >= itemBeingBought.Price) | |
{ | |
// Add one of the items to the player's inventory | |
_currentPlayer.AddItemToInventory(itemBeingBought); | |
// Remove the gold to pay for the item | |
_currentPlayer.Gold -= itemBeingBought.Price; | |
} | |
else | |
{ | |
MessageBox.Show("You do not have enough gold to buy the " + itemBeingBought.Name); | |
} | |
} | |
} | |
private void btnClose_Click(object sender, EventArgs e) | |
{ | |
Close(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
using System.Linq; | |
namespace Engine | |
{ | |
public class Vendor : INotifyPropertyChanged | |
{ | |
public string Name { get; set; } | |
public BindingList<InventoryItem> Inventory { get; set; } | |
public Vendor(string name) | |
{ | |
Name = name; | |
Inventory = new BindingList<InventoryItem>(); | |
} | |
public void AddItemToInventory(Item itemToAdd, int quantity = 1) | |
{ | |
InventoryItem item = Inventory.SingleOrDefault(ii => ii.Details.ID == itemToAdd.ID); | |
if(item == null) | |
{ | |
// They didn't have the item, so add it to their inventory | |
Inventory.Add(new InventoryItem(itemToAdd, quantity)); | |
} | |
else | |
{ | |
// They have the item in their inventory, so increase the quantity | |
item.Quantity += quantity; | |
} | |
OnPropertyChanged("Inventory"); | |
} | |
public void RemoveItemFromInventory(Item itemToRemove, int quantity = 1) | |
{ | |
InventoryItem item = Inventory.SingleOrDefault(ii => ii.Details.ID == itemToRemove.ID); | |
if(item == null) | |
{ | |
// The item is not in the player's inventory, so ignore it. | |
// We might want to raise an error for this situation | |
} | |
else | |
{ | |
// They have the item in their inventory, so decrease the quantity | |
item.Quantity -= quantity; | |
// Don't allow negative quantities. | |
// We might want to raise an error for this situation | |
if(item.Quantity < 0) | |
{ | |
item.Quantity = 0; | |
} | |
// If the quantity is zero, remove the item from the list | |
if(item.Quantity == 0) | |
{ | |
Inventory.Remove(item); | |
} | |
// Notify the UI that the inventory has changed | |
OnPropertyChanged("Inventory"); | |
} | |
} | |
public event PropertyChangedEventHandler PropertyChanged; | |
private void OnPropertyChanged(string name) | |
{ | |
if(PropertyChanged != null) | |
{ | |
PropertyChanged(this, new PropertyChangedEventArgs(name)); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Linq; | |
namespace Engine | |
{ | |
public static class World | |
{ | |
private static readonly List<Item> _items = new List<Item>(); | |
private static readonly List<Monster> _monsters = new List<Monster>(); | |
private static readonly List<Quest> _quests = new List<Quest>(); | |
private static readonly List<Location> _locations = new List<Location>(); | |
public const int UNSELLABLE_ITEM_PRICE = -1; | |
public const int ITEM_ID_RUSTY_SWORD = 1; | |
public const int ITEM_ID_RAT_TAIL = 2; | |
public const int ITEM_ID_PIECE_OF_FUR = 3; | |
public const int ITEM_ID_SNAKE_FANG = 4; | |
public const int ITEM_ID_SNAKESKIN = 5; | |
public const int ITEM_ID_CLUB = 6; | |
public const int ITEM_ID_HEALING_POTION = 7; | |
public const int ITEM_ID_SPIDER_FANG = 8; | |
public const int ITEM_ID_SPIDER_SILK = 9; | |
public const int ITEM_ID_ADVENTURER_PASS = 10; | |
public const int MONSTER_ID_RAT = 1; | |
public const int MONSTER_ID_SNAKE = 2; | |
public const int MONSTER_ID_GIANT_SPIDER = 3; | |
public const int QUEST_ID_CLEAR_ALCHEMIST_GARDEN = 1; | |
public const int QUEST_ID_CLEAR_FARMERS_FIELD = 2; | |
public const int LOCATION_ID_HOME = 1; | |
public const int LOCATION_ID_TOWN_SQUARE = 2; | |
public const int LOCATION_ID_GUARD_POST = 3; | |
public const int LOCATION_ID_ALCHEMIST_HUT = 4; | |
public const int LOCATION_ID_ALCHEMISTS_GARDEN = 5; | |
public const int LOCATION_ID_FARMHOUSE = 6; | |
public const int LOCATION_ID_FARM_FIELD = 7; | |
public const int LOCATION_ID_BRIDGE = 8; | |
public const int LOCATION_ID_SPIDER_FIELD = 9; | |
static World() | |
{ | |
PopulateItems(); | |
PopulateMonsters(); | |
PopulateQuests(); | |
PopulateLocations(); | |
} | |
private static void PopulateItems() | |
{ | |
_items.Add(new Weapon(ITEM_ID_RUSTY_SWORD, "Rusty sword", "Rusty swords", 0, 5, 5)); | |
_items.Add(new Item(ITEM_ID_RAT_TAIL, "Rat tail", "Rat tails", 1)); | |
_items.Add(new Item(ITEM_ID_PIECE_OF_FUR, "Piece of fur", "Pieces of fur", 1)); | |
_items.Add(new Item(ITEM_ID_SNAKE_FANG, "Snake fang", "Snake fangs", 1)); | |
_items.Add(new Item(ITEM_ID_SNAKESKIN, "Snakeskin", "Snakeskins", 2)); | |
_items.Add(new Weapon(ITEM_ID_CLUB, "Club", "Clubs", 3, 10, 8)); | |
_items.Add(new HealingPotion(ITEM_ID_HEALING_POTION, "Healing potion", "Healing potions", 5, 3)); | |
_items.Add(new Item(ITEM_ID_SPIDER_FANG, "Spider fang", "Spider fangs", 1)); | |
_items.Add(new Item(ITEM_ID_SPIDER_SILK, "Spider silk", "Spider silks", 1)); | |
_items.Add(new Item(ITEM_ID_ADVENTURER_PASS, "Adventurer pass", "Adventurer passes", UNSELLABLE_ITEM_PRICE)); | |
} | |
private static void PopulateMonsters() | |
{ | |
Monster rat = new Monster(MONSTER_ID_RAT, "Rat", 5, 3, 10, 3, 3); | |
rat.LootTable.Add(new LootItem(ItemByID(ITEM_ID_RAT_TAIL), 75, false)); | |
rat.LootTable.Add(new LootItem(ItemByID(ITEM_ID_PIECE_OF_FUR), 75, true)); | |
Monster snake = new Monster(MONSTER_ID_SNAKE, "Snake", 5, 3, 10, 3, 3); | |
snake.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SNAKE_FANG), 75, false)); | |
snake.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SNAKESKIN), 75, true)); | |
Monster giantSpider = new Monster(MONSTER_ID_GIANT_SPIDER, "Giant spider", 20, 5, 40, 10, 10); | |
giantSpider.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SPIDER_FANG), 75, true)); | |
giantSpider.LootTable.Add(new LootItem(ItemByID(ITEM_ID_SPIDER_SILK), 25, false)); | |
_monsters.Add(rat); | |
_monsters.Add(snake); | |
_monsters.Add(giantSpider); | |
} | |
private static void PopulateQuests() | |
{ | |
Quest clearAlchemistGarden = | |
new Quest( | |
QUEST_ID_CLEAR_ALCHEMIST_GARDEN, | |
"Clear the alchemist's garden", | |
"Kill rats in the alchemist's garden and bring back 3 rat tails. You will receive a healing potion and 10 gold pieces.", 20, 10); | |
clearAlchemistGarden.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_RAT_TAIL), 3)); | |
clearAlchemistGarden.RewardItem = ItemByID(ITEM_ID_HEALING_POTION); | |
Quest clearFarmersField = | |
new Quest( | |
QUEST_ID_CLEAR_FARMERS_FIELD, | |
"Clear the farmer's field", | |
"Kill snakes in the farmer's field and bring back 3 snake fangs. You will receive an adventurer's pass and 20 gold pieces.", 20, 20); | |
clearFarmersField.QuestCompletionItems.Add(new QuestCompletionItem(ItemByID(ITEM_ID_SNAKE_FANG), 3)); | |
clearFarmersField.RewardItem = ItemByID(ITEM_ID_ADVENTURER_PASS); | |
_quests.Add(clearAlchemistGarden); | |
_quests.Add(clearFarmersField); | |
} | |
private static void PopulateLocations() | |
{ | |
// Create each location | |
Location home = new Location(LOCATION_ID_HOME, "Home", "Your house. You really need to clean up the place."); | |
Location townSquare = new Location(LOCATION_ID_TOWN_SQUARE, "Town square", "You see a fountain."); | |
Vendor bobTheRatCatcher = new Vendor("Bob the Rat-Catcher"); | |
bobTheRatCatcher.AddItemToInventory(ItemByID(ITEM_ID_PIECE_OF_FUR), 5); | |
bobTheRatCatcher.AddItemToInventory(ItemByID(ITEM_ID_RAT_TAIL), 3); | |
townSquare.VendorWorkingHere = bobTheRatCatcher; | |
Location alchemistHut = new Location(LOCATION_ID_ALCHEMIST_HUT, "Alchemist's hut", "There are many strange plants on the shelves."); | |
alchemistHut.QuestAvailableHere = QuestByID(QUEST_ID_CLEAR_ALCHEMIST_GARDEN); | |
Location alchemistsGarden = new Location(LOCATION_ID_ALCHEMISTS_GARDEN, "Alchemist's garden", "Many plants are growing here."); | |
alchemistsGarden.MonsterLivingHere = MonsterByID(MONSTER_ID_RAT); | |
Location farmhouse = new Location(LOCATION_ID_FARMHOUSE, "Farmhouse", "There is a small farmhouse, with a farmer in front."); | |
farmhouse.QuestAvailableHere = QuestByID(QUEST_ID_CLEAR_FARMERS_FIELD); | |
Location farmersField = new Location(LOCATION_ID_FARM_FIELD, "Farmer's field", "You see rows of vegetables growing here."); | |
farmersField.MonsterLivingHere = MonsterByID(MONSTER_ID_SNAKE); | |
Location guardPost = new Location(LOCATION_ID_GUARD_POST, "Guard post", "There is a large, tough-looking guard here.", ItemByID(ITEM_ID_ADVENTURER_PASS)); | |
Location bridge = new Location(LOCATION_ID_BRIDGE, "Bridge", "A stone bridge crosses a wide river."); | |
Location spiderField = new Location(LOCATION_ID_SPIDER_FIELD, "Forest", "You see spider webs covering covering the trees in this forest."); | |
spiderField.MonsterLivingHere = MonsterByID(MONSTER_ID_GIANT_SPIDER); | |
// Link the locations together | |
home.LocationToNorth = townSquare; | |
townSquare.LocationToNorth = alchemistHut; | |
townSquare.LocationToSouth = home; | |
townSquare.LocationToEast = guardPost; | |
townSquare.LocationToWest = farmhouse; | |
farmhouse.LocationToEast = townSquare; | |
farmhouse.LocationToWest = farmersField; | |
farmersField.LocationToEast = farmhouse; | |
alchemistHut.LocationToSouth = townSquare; | |
alchemistHut.LocationToNorth = alchemistsGarden; | |
alchemistsGarden.LocationToSouth = alchemistHut; | |
guardPost.LocationToEast = bridge; | |
guardPost.LocationToWest = townSquare; | |
bridge.LocationToWest = guardPost; | |
bridge.LocationToEast = spiderField; | |
spiderField.LocationToWest = bridge; | |
// Add the locations to the static list | |
_locations.Add(home); | |
_locations.Add(townSquare); | |
_locations.Add(guardPost); | |
_locations.Add(alchemistHut); | |
_locations.Add(alchemistsGarden); | |
_locations.Add(farmhouse); | |
_locations.Add(farmersField); | |
_locations.Add(bridge); | |
_locations.Add(spiderField); | |
} | |
public static Item ItemByID(int id) | |
{ | |
return _items.SingleOrDefault(x => x.ID == id); | |
} | |
public static Monster MonsterByID(int id) | |
{ | |
return _monsters.SingleOrDefault(x => x.ID == id); | |
} | |
public static Quest QuestByID(int id) | |
{ | |
return _quests.SingleOrDefault(x => x.ID == id); | |
} | |
public static Location LocationByID(int id) | |
{ | |
return _locations.SingleOrDefault(x => x.ID == id); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment