//-----------------------------------------------------------------------------
// Quest.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
namespace RolePlaying.Data
{
///
/// A quest that the party can embark on, with goals and rewards.
///
public class Quest : ContentObject
#if WINDOWS
, ICloneable
#endif
{
///
/// The possible stages of a quest.
///
public enum QuestStage
{
NotStarted,
InProgress,
RequirementsMet,
Completed
};
///
/// The current stage of this quest.
///
private QuestStage stage = QuestStage.NotStarted;
///
/// The current stage of this quest.
///
[ContentSerializerIgnore]
public QuestStage Stage
{
get { return stage; }
set { stage = value; }
}
///
/// The name of the quest.
///
private string name;
///
/// The name of the quest.
///
public string Name
{
get { return name; }
set { name = value; }
}
///
/// A description of the quest.
///
private string description;
///
/// A description of the quest.
///
public string Description
{
get { return description; }
set { description = value; }
}
///
/// A message describing the objective of the quest,
/// presented when the player receives the quest.
///
private string objectiveMessage;
///
/// A message describing the objective of the quest,
/// presented when the player receives the quest.
///
public string ObjectiveMessage
{
get { return objectiveMessage; }
set { objectiveMessage = value; }
}
///
/// A message announcing the completion of the quest,
/// presented when the player reaches the goals of the quest.
///
private string completionMessage;
public string CompletionMessage
{
get { return completionMessage; }
set { completionMessage = value; }
}
///
/// The gear that the player must have to finish the quest.
///
private List> gearRequirements =
new List>();
///
/// The gear that the player must have to finish the quest.
///
public List> GearRequirements
{
get { return gearRequirements; }
set { gearRequirements = value; }
}
///
/// The monsters that must be killed to finish the quest.
///
private List> monsterRequirements =
new List>();
///
/// The monsters that must be killed to finish the quest.
///
public List> MonsterRequirements
{
get { return monsterRequirements; }
set { monsterRequirements = value; }
}
///
/// Returns true if all requirements for this quest have been met.
///
public bool AreRequirementsMet
{
get
{
foreach (QuestRequirement gearRequirement in gearRequirements)
{
if (gearRequirement.CompletedCount < gearRequirement.Count)
{
return false;
}
}
foreach (QuestRequirement monsterRequirement
in monsterRequirements)
{
if (monsterRequirement.CompletedCount < monsterRequirement.Count)
{
return false;
}
}
return true;
}
}
///
/// The fixed combat encounters added to the world when this quest is active.
///
private List> fixedCombatEntries =
new List>();
///
/// The fixed combat encounters added to the world when this quest is active.
///
public List> FixedCombatEntries
{
get { return fixedCombatEntries; }
set { fixedCombatEntries = value; }
}
///
/// The chests added to thew orld when this quest is active.
///
private List> chestEntries = new List>();
///
/// The chests added to thew orld when this quest is active.
///
public List> ChestEntries
{
get { return chestEntries; }
set { chestEntries = value; }
}
///
/// The map with the destination Npc, if any.
///
private string destinationMapContentName;
///
/// The map with the destination Npc, if any.
///
[ContentSerializer(Optional = true)]
public string DestinationMapContentName
{
get { return destinationMapContentName; }
set { destinationMapContentName = value; }
}
///
/// The Npc that the party must visit to finish the quest, if any.
///
private string destinationNpcContentName;
///
/// The Npc that the party must visit to finish the quest, if any.
///
[ContentSerializer(Optional = true)]
public string DestinationNpcContentName
{
get { return destinationNpcContentName; }
set { destinationNpcContentName = value; }
}
///
/// The message shown when the party is eligible to complete the quest, if any.
///
private string destinationObjectiveMessage;
///
/// The message shown when the party is eligible to complete the quest, if any.
///
[ContentSerializer(Optional = true)]
public string DestinationObjectiveMessage
{
get { return destinationObjectiveMessage; }
set { destinationObjectiveMessage = value; }
}
///
/// The number of experience points given to each party member as a reward.
///
private int experienceReward;
///
/// The number of experience points given to each party member as a reward.
///
[ContentSerializer(Optional = true)]
public int ExperienceReward
{
get { return experienceReward; }
set { experienceReward = value; }
}
///
/// The amount of gold given to the party as a reward.
///
private int goldReward;
///
/// The amount of gold given to the party as a reward.
///
[ContentSerializer(Optional = true)]
public int GoldReward
{
get { return goldReward; }
set { goldReward = value; }
}
///
/// The content names of the gear given to the party as a reward.
///
private List gearRewardContentNames = new List();
///
/// The content names of the gear given to the party as a reward.
///
[ContentSerializer(Optional = true)]
public List GearRewardContentNames
{
get { return gearRewardContentNames; }
set { gearRewardContentNames = value; }
}
///
/// The gear given to the party as a reward.
///
private List gearRewards = new List();
///
/// The gear given to the party as a reward.
///
[ContentSerializerIgnore]
public List GearRewards
{
get { return gearRewards; }
set { gearRewards = value; }
}
///
/// Reads a Quest object from the content pipeline.
///
public class QuestReader : ContentTypeReader
{
///
/// Reads a Quest object from the content pipeline.
///
protected override Quest Read(ContentReader input, Quest existingInstance)
{
Quest quest = existingInstance;
if (quest == null)
{
quest = new Quest();
}
quest.AssetName = input.AssetName;
quest.Name = input.ReadString();
quest.Description = input.ReadString();
quest.ObjectiveMessage = input.ReadString();
quest.CompletionMessage = input.ReadString();
quest.GearRequirements.AddRange(
input.ReadObject>>());
quest.MonsterRequirements.AddRange(
input.ReadObject>>());
// load the fixed combat entries
Random random = new Random();
quest.FixedCombatEntries.AddRange(
input.ReadObject>>());
foreach (WorldEntry fixedCombatEntry in
quest.FixedCombatEntries)
{
fixedCombatEntry.Content =
input.ContentManager.Load(Path.Combine("Maps", "FixedCombats", fixedCombatEntry.ContentName));
// clone the map sprite in the entry, as there may be many entries
// per FixedCombat
fixedCombatEntry.MapSprite =
fixedCombatEntry.Content.Entries[0].Content.MapSprite.Clone()
as AnimatingSprite;
// play the idle animation
fixedCombatEntry.MapSprite.PlayAnimation("Idle",
fixedCombatEntry.Direction);
// advance in a random amount so the animations aren't synchronized
fixedCombatEntry.MapSprite.UpdateAnimation(
4f * (float)random.NextDouble());
}
quest.ChestEntries.AddRange(
input.ReadObject>>());
foreach (WorldEntry chestEntry in quest.ChestEntries)
{
chestEntry.Content = input.ContentManager.Load(Path.Combine("Maps", "Chests", chestEntry.ContentName)).Clone() as Chest;
}
quest.DestinationMapContentName = input.ReadString();
quest.DestinationNpcContentName = input.ReadString();
quest.DestinationObjectiveMessage = input.ReadString();
quest.experienceReward = input.ReadInt32();
quest.goldReward = input.ReadInt32();
quest.GearRewardContentNames.AddRange(
input.ReadObject>());
foreach (string contentName in quest.GearRewardContentNames)
{
quest.GearRewards.Add(input.ContentManager.Load(Path.Combine("Gear", contentName)));
}
return quest;
}
}
public object Clone()
{
Quest quest = new Quest();
quest.AssetName = AssetName;
foreach (WorldEntry chestEntry in chestEntries)
{
WorldEntry worldEntry = new WorldEntry();
worldEntry.Content = chestEntry.Content.Clone() as Chest;
worldEntry.ContentName = chestEntry.ContentName;
worldEntry.Count = chestEntry.Count;
worldEntry.Direction = chestEntry.Direction;
worldEntry.MapContentName = chestEntry.MapContentName;
worldEntry.MapPosition = chestEntry.MapPosition;
quest.chestEntries.Add(worldEntry);
}
quest.completionMessage = completionMessage;
quest.description = description;
quest.destinationMapContentName = destinationMapContentName;
quest.destinationNpcContentName = destinationNpcContentName;
quest.destinationObjectiveMessage = destinationObjectiveMessage;
quest.experienceReward = experienceReward;
quest.fixedCombatEntries.AddRange(fixedCombatEntries);
quest.gearRequirements.AddRange(gearRequirements);
quest.gearRewardContentNames.AddRange(gearRewardContentNames);
quest.gearRewards.AddRange(gearRewards);
quest.goldReward = goldReward;
quest.monsterRequirements.AddRange(monsterRequirements);
quest.name = name;
quest.objectiveMessage = objectiveMessage;
quest.stage = stage;
return quest;
}
internal static Quest Load(string questContentName, ContentManager contentManager)
{
var questElement = XmlHelper.GetAssetElementFromXML(Path.Combine("Quests", questContentName));
var quest = new Quest
{
AssetName = questContentName,
Name = (string)questElement.Element("Name"),
Description = (string)questElement.Element("Description"),
ObjectiveMessage = (string)questElement.Element("ObjectiveMessage"),
CompletionMessage = (string)questElement.Element("CompletionMessage"),
DestinationMapContentName = (string)questElement.Element("DestinationMapContentName"),
DestinationNpcContentName = (string)questElement.Element("DestinationNpcContentName"),
DestinationObjectiveMessage = (string)questElement.Element("DestinationObjectiveMessage"),
ExperienceReward = (int?)questElement.Element("ExperienceReward") ?? 0,
GoldReward = (int?)questElement.Element("GoldReward") ?? 0,
Stage = Enum.TryParse((string)questElement.Element("Stage"), out var stage) ? stage : QuestStage.NotStarted,
GearRewardContentNames = questElement.Element("GearRewardContentNames")?
.Elements("Item")
.Select(x => (string)x)
.ToList() ?? new List(),
GearRequirements = questElement.Element("GearRequirements")?
.Elements("Item")
.Select(gearElement =>
{
var gearContentName = (string)gearElement.Element("ContentName");
var gearCount = (int?)gearElement.Element("Count") ?? 1;
var gear = Equipment.Load(Path.Combine("Gear", gearContentName), contentManager);
return new QuestRequirement
{
ContentName = gearContentName,
Count = gearCount,
Content = gear
};
}).ToList() ?? new List>(),
MonsterRequirements = questElement.Element("MonsterRequirements")?
.Elements("Item")
.Select(entry =>
{
var monsterContentName = (string)entry.Element("ContentName");
var monsterCount = (int?)entry.Element("Count") ?? 1;
var monster = Monster.Load(Path.Combine("Characters", "Monsters", monsterContentName), contentManager);
return new QuestRequirement
{
ContentName = monsterContentName,
Count = monsterCount,
Content = monster
};
}).ToList() ?? new List>(),
FixedCombatEntries = questElement.Element("FixedCombatEntries")?.Elements("Item")
.Select(item =>
{
var contentName = (string)item.Element("ContentName");
var direction = Enum.TryParse((string)item.Element("Direction"), out var dir) ? dir : default;
var mapPosition = new Point(
int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
// Load the fixed combat asset XML using contentName
var fixedCombat = FixedCombat.Load(Path.Combine("Maps", "FixedCombats", contentName), contentManager);
AnimatingSprite animatingSprite = null;
if (fixedCombat.Entries.Count > 0)
{
animatingSprite = fixedCombat.Entries[0].Content.MapSprite.Clone() as AnimatingSprite;
}
return new WorldEntry
{
ContentName = contentName,
Content = fixedCombat,
Direction = direction,
MapPosition = mapPosition,
MapSprite = animatingSprite,
};
}).ToList() ?? new List>(),
ChestEntries = questElement.Element("ChestEntries")?
.Elements("Item")
.Select(chestRequirement =>
{
var contentName = (string)chestRequirement.Element("ContentName");
var direction = Enum.TryParse((string)chestRequirement.Element("Direction"), out var dir) ? dir : default;
var mapPosition = new Point(
int.Parse(chestRequirement.Element("MapPosition").Value.Split(' ')[0]),
int.Parse(chestRequirement.Element("MapPosition").Value.Split(' ')[1]));
var mapContentName = (string)chestRequirement.Element("MapContentName");
var count = (int?)chestRequirement.Element("Count") ?? 1;
// Load the QuestNpc asset XML using contentName
var chestAsset = XmlHelper.GetAssetElementFromXML(Path.Combine("Maps", "Chests", contentName));
var chest = Chest.Load(chestAsset, contentManager);
return new WorldEntry
{
ContentName = contentName,
Content = chest,
Count = count,
Direction = direction,
MapContentName = mapContentName,
MapPosition = mapPosition,
MapSprite = null,
};
}).ToList() ?? new List>(),
};
// Load the gear rewards
foreach (var gearContentName in quest.GearRewardContentNames)
{
var gear = Equipment.Load(Path.Combine("Gear", gearContentName), contentManager);
quest.GearRewards.Add(gear);
}
return quest;
}
}
}