Quest.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. //-----------------------------------------------------------------------------
  2. // Quest.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Xml.Linq;
  12. using Microsoft.Xna.Framework;
  13. using Microsoft.Xna.Framework.Content;
  14. namespace RolePlaying.Data
  15. {
  16. /// <summary>
  17. /// A quest that the party can embark on, with goals and rewards.
  18. /// </summary>
  19. public class Quest : ContentObject
  20. #if WINDOWS
  21. , ICloneable
  22. #endif
  23. {
  24. /// <summary>
  25. /// The possible stages of a quest.
  26. /// </summary>
  27. public enum QuestStage
  28. {
  29. NotStarted,
  30. InProgress,
  31. RequirementsMet,
  32. Completed
  33. };
  34. /// <summary>
  35. /// The current stage of this quest.
  36. /// </summary>
  37. private QuestStage stage = QuestStage.NotStarted;
  38. /// <summary>
  39. /// The current stage of this quest.
  40. /// </summary>
  41. [ContentSerializerIgnore]
  42. public QuestStage Stage
  43. {
  44. get { return stage; }
  45. set { stage = value; }
  46. }
  47. /// <summary>
  48. /// The name of the quest.
  49. /// </summary>
  50. private string name;
  51. /// <summary>
  52. /// The name of the quest.
  53. /// </summary>
  54. public string Name
  55. {
  56. get { return name; }
  57. set { name = value; }
  58. }
  59. /// <summary>
  60. /// A description of the quest.
  61. /// </summary>
  62. private string description;
  63. /// <summary>
  64. /// A description of the quest.
  65. /// </summary>
  66. public string Description
  67. {
  68. get { return description; }
  69. set { description = value; }
  70. }
  71. /// <summary>
  72. /// A message describing the objective of the quest,
  73. /// presented when the player receives the quest.
  74. /// </summary>
  75. private string objectiveMessage;
  76. /// <summary>
  77. /// A message describing the objective of the quest,
  78. /// presented when the player receives the quest.
  79. /// </summary>
  80. public string ObjectiveMessage
  81. {
  82. get { return objectiveMessage; }
  83. set { objectiveMessage = value; }
  84. }
  85. /// <summary>
  86. /// A message announcing the completion of the quest,
  87. /// presented when the player reaches the goals of the quest.
  88. /// </summary>
  89. private string completionMessage;
  90. public string CompletionMessage
  91. {
  92. get { return completionMessage; }
  93. set { completionMessage = value; }
  94. }
  95. /// <summary>
  96. /// The gear that the player must have to finish the quest.
  97. /// </summary>
  98. private List<QuestRequirement<Gear>> gearRequirements =
  99. new List<QuestRequirement<Gear>>();
  100. /// <summary>
  101. /// The gear that the player must have to finish the quest.
  102. /// </summary>
  103. public List<QuestRequirement<Gear>> GearRequirements
  104. {
  105. get { return gearRequirements; }
  106. set { gearRequirements = value; }
  107. }
  108. /// <summary>
  109. /// The monsters that must be killed to finish the quest.
  110. /// </summary>
  111. private List<QuestRequirement<Monster>> monsterRequirements =
  112. new List<QuestRequirement<Monster>>();
  113. /// <summary>
  114. /// The monsters that must be killed to finish the quest.
  115. /// </summary>
  116. public List<QuestRequirement<Monster>> MonsterRequirements
  117. {
  118. get { return monsterRequirements; }
  119. set { monsterRequirements = value; }
  120. }
  121. /// <summary>
  122. /// Returns true if all requirements for this quest have been met.
  123. /// </summary>
  124. public bool AreRequirementsMet
  125. {
  126. get
  127. {
  128. foreach (QuestRequirement<Gear> gearRequirement in gearRequirements)
  129. {
  130. if (gearRequirement.CompletedCount < gearRequirement.Count)
  131. {
  132. return false;
  133. }
  134. }
  135. foreach (QuestRequirement<Monster> monsterRequirement
  136. in monsterRequirements)
  137. {
  138. if (monsterRequirement.CompletedCount < monsterRequirement.Count)
  139. {
  140. return false;
  141. }
  142. }
  143. return true;
  144. }
  145. }
  146. /// <summary>
  147. /// The fixed combat encounters added to the world when this quest is active.
  148. /// </summary>
  149. private List<WorldEntry<FixedCombat>> fixedCombatEntries =
  150. new List<WorldEntry<FixedCombat>>();
  151. /// <summary>
  152. /// The fixed combat encounters added to the world when this quest is active.
  153. /// </summary>
  154. public List<WorldEntry<FixedCombat>> FixedCombatEntries
  155. {
  156. get { return fixedCombatEntries; }
  157. set { fixedCombatEntries = value; }
  158. }
  159. /// <summary>
  160. /// The chests added to thew orld when this quest is active.
  161. /// </summary>
  162. private List<WorldEntry<Chest>> chestEntries = new List<WorldEntry<Chest>>();
  163. /// <summary>
  164. /// The chests added to thew orld when this quest is active.
  165. /// </summary>
  166. public List<WorldEntry<Chest>> ChestEntries
  167. {
  168. get { return chestEntries; }
  169. set { chestEntries = value; }
  170. }
  171. /// <summary>
  172. /// The map with the destination Npc, if any.
  173. /// </summary>
  174. private string destinationMapContentName;
  175. /// <summary>
  176. /// The map with the destination Npc, if any.
  177. /// </summary>
  178. [ContentSerializer(Optional = true)]
  179. public string DestinationMapContentName
  180. {
  181. get { return destinationMapContentName; }
  182. set { destinationMapContentName = value; }
  183. }
  184. /// <summary>
  185. /// The Npc that the party must visit to finish the quest, if any.
  186. /// </summary>
  187. private string destinationNpcContentName;
  188. /// <summary>
  189. /// The Npc that the party must visit to finish the quest, if any.
  190. /// </summary>
  191. [ContentSerializer(Optional = true)]
  192. public string DestinationNpcContentName
  193. {
  194. get { return destinationNpcContentName; }
  195. set { destinationNpcContentName = value; }
  196. }
  197. /// <summary>
  198. /// The message shown when the party is eligible to complete the quest, if any.
  199. /// </summary>
  200. private string destinationObjectiveMessage;
  201. /// <summary>
  202. /// The message shown when the party is eligible to complete the quest, if any.
  203. /// </summary>
  204. [ContentSerializer(Optional = true)]
  205. public string DestinationObjectiveMessage
  206. {
  207. get { return destinationObjectiveMessage; }
  208. set { destinationObjectiveMessage = value; }
  209. }
  210. /// <summary>
  211. /// The number of experience points given to each party member as a reward.
  212. /// </summary>
  213. private int experienceReward;
  214. /// <summary>
  215. /// The number of experience points given to each party member as a reward.
  216. /// </summary>
  217. [ContentSerializer(Optional = true)]
  218. public int ExperienceReward
  219. {
  220. get { return experienceReward; }
  221. set { experienceReward = value; }
  222. }
  223. /// <summary>
  224. /// The amount of gold given to the party as a reward.
  225. /// </summary>
  226. private int goldReward;
  227. /// <summary>
  228. /// The amount of gold given to the party as a reward.
  229. /// </summary>
  230. [ContentSerializer(Optional = true)]
  231. public int GoldReward
  232. {
  233. get { return goldReward; }
  234. set { goldReward = value; }
  235. }
  236. /// <summary>
  237. /// The content names of the gear given to the party as a reward.
  238. /// </summary>
  239. private List<string> gearRewardContentNames = new List<string>();
  240. /// <summary>
  241. /// The content names of the gear given to the party as a reward.
  242. /// </summary>
  243. [ContentSerializer(Optional = true)]
  244. public List<string> GearRewardContentNames
  245. {
  246. get { return gearRewardContentNames; }
  247. set { gearRewardContentNames = value; }
  248. }
  249. /// <summary>
  250. /// The gear given to the party as a reward.
  251. /// </summary>
  252. private List<Gear> gearRewards = new List<Gear>();
  253. /// <summary>
  254. /// The gear given to the party as a reward.
  255. /// </summary>
  256. [ContentSerializerIgnore]
  257. public List<Gear> GearRewards
  258. {
  259. get { return gearRewards; }
  260. set { gearRewards = value; }
  261. }
  262. /// <summary>
  263. /// Reads a Quest object from the content pipeline.
  264. /// </summary>
  265. public class QuestReader : ContentTypeReader<Quest>
  266. {
  267. /// <summary>
  268. /// Reads a Quest object from the content pipeline.
  269. /// </summary>
  270. protected override Quest Read(ContentReader input, Quest existingInstance)
  271. {
  272. Quest quest = existingInstance;
  273. if (quest == null)
  274. {
  275. quest = new Quest();
  276. }
  277. quest.AssetName = input.AssetName;
  278. quest.Name = input.ReadString();
  279. quest.Description = input.ReadString();
  280. quest.ObjectiveMessage = input.ReadString();
  281. quest.CompletionMessage = input.ReadString();
  282. quest.GearRequirements.AddRange(
  283. input.ReadObject<List<QuestRequirement<Gear>>>());
  284. quest.MonsterRequirements.AddRange(
  285. input.ReadObject<List<QuestRequirement<Monster>>>());
  286. // load the fixed combat entries
  287. Random random = new Random();
  288. quest.FixedCombatEntries.AddRange(
  289. input.ReadObject<List<WorldEntry<FixedCombat>>>());
  290. foreach (WorldEntry<FixedCombat> fixedCombatEntry in
  291. quest.FixedCombatEntries)
  292. {
  293. fixedCombatEntry.Content =
  294. input.ContentManager.Load<FixedCombat>(Path.Combine("Maps", "FixedCombats", fixedCombatEntry.ContentName));
  295. // clone the map sprite in the entry, as there may be many entries
  296. // per FixedCombat
  297. fixedCombatEntry.MapSprite =
  298. fixedCombatEntry.Content.Entries[0].Content.MapSprite.Clone()
  299. as AnimatingSprite;
  300. // play the idle animation
  301. fixedCombatEntry.MapSprite.PlayAnimation("Idle",
  302. fixedCombatEntry.Direction);
  303. // advance in a random amount so the animations aren't synchronized
  304. fixedCombatEntry.MapSprite.UpdateAnimation(
  305. 4f * (float)random.NextDouble());
  306. }
  307. quest.ChestEntries.AddRange(
  308. input.ReadObject<List<WorldEntry<Chest>>>());
  309. foreach (WorldEntry<Chest> chestEntry in quest.ChestEntries)
  310. {
  311. chestEntry.Content = input.ContentManager.Load<Chest>(Path.Combine("Maps", "Chests", chestEntry.ContentName)).Clone() as Chest;
  312. }
  313. quest.DestinationMapContentName = input.ReadString();
  314. quest.DestinationNpcContentName = input.ReadString();
  315. quest.DestinationObjectiveMessage = input.ReadString();
  316. quest.experienceReward = input.ReadInt32();
  317. quest.goldReward = input.ReadInt32();
  318. quest.GearRewardContentNames.AddRange(
  319. input.ReadObject<List<string>>());
  320. foreach (string contentName in quest.GearRewardContentNames)
  321. {
  322. quest.GearRewards.Add(input.ContentManager.Load<Gear>(Path.Combine("Gear", contentName)));
  323. }
  324. return quest;
  325. }
  326. }
  327. public object Clone()
  328. {
  329. Quest quest = new Quest();
  330. quest.AssetName = AssetName;
  331. foreach (WorldEntry<Chest> chestEntry in chestEntries)
  332. {
  333. WorldEntry<Chest> worldEntry = new WorldEntry<Chest>();
  334. worldEntry.Content = chestEntry.Content.Clone() as Chest;
  335. worldEntry.ContentName = chestEntry.ContentName;
  336. worldEntry.Count = chestEntry.Count;
  337. worldEntry.Direction = chestEntry.Direction;
  338. worldEntry.MapContentName = chestEntry.MapContentName;
  339. worldEntry.MapPosition = chestEntry.MapPosition;
  340. quest.chestEntries.Add(worldEntry);
  341. }
  342. quest.completionMessage = completionMessage;
  343. quest.description = description;
  344. quest.destinationMapContentName = destinationMapContentName;
  345. quest.destinationNpcContentName = destinationNpcContentName;
  346. quest.destinationObjectiveMessage = destinationObjectiveMessage;
  347. quest.experienceReward = experienceReward;
  348. quest.fixedCombatEntries.AddRange(fixedCombatEntries);
  349. quest.gearRequirements.AddRange(gearRequirements);
  350. quest.gearRewardContentNames.AddRange(gearRewardContentNames);
  351. quest.gearRewards.AddRange(gearRewards);
  352. quest.goldReward = goldReward;
  353. quest.monsterRequirements.AddRange(monsterRequirements);
  354. quest.name = name;
  355. quest.objectiveMessage = objectiveMessage;
  356. quest.stage = stage;
  357. return quest;
  358. }
  359. internal static Quest Load(string questContentName, ContentManager contentManager)
  360. {
  361. var questElement = XmlHelper.GetAssetElementFromXML(Path.Combine("Quests", questContentName));
  362. var quest = new Quest
  363. {
  364. AssetName = questContentName,
  365. Name = (string)questElement.Element("Name"),
  366. Description = (string)questElement.Element("Description"),
  367. ObjectiveMessage = (string)questElement.Element("ObjectiveMessage"),
  368. CompletionMessage = (string)questElement.Element("CompletionMessage"),
  369. DestinationMapContentName = (string)questElement.Element("DestinationMapContentName"),
  370. DestinationNpcContentName = (string)questElement.Element("DestinationNpcContentName"),
  371. DestinationObjectiveMessage = (string)questElement.Element("DestinationObjectiveMessage"),
  372. ExperienceReward = (int?)questElement.Element("ExperienceReward") ?? 0,
  373. GoldReward = (int?)questElement.Element("GoldReward") ?? 0,
  374. Stage = Enum.TryParse<QuestStage>((string)questElement.Element("Stage"), out var stage) ? stage : QuestStage.NotStarted,
  375. GearRewardContentNames = questElement.Element("GearRewardContentNames")?
  376. .Elements("Item")
  377. .Select(x => (string)x)
  378. .ToList() ?? new List<string>(),
  379. GearRequirements = questElement.Element("GearRequirements")?
  380. .Elements("Item")
  381. .Select(gearElement =>
  382. {
  383. var gearContentName = (string)gearElement.Element("ContentName");
  384. var gearCount = (int?)gearElement.Element("Count") ?? 1;
  385. var gear = Equipment.Load(Path.Combine("Gear", gearContentName), contentManager);
  386. return new QuestRequirement<Gear>
  387. {
  388. ContentName = gearContentName,
  389. Count = gearCount,
  390. Content = gear
  391. };
  392. }).ToList() ?? new List<QuestRequirement<Gear>>(),
  393. MonsterRequirements = questElement.Element("MonsterRequirements")?
  394. .Elements("Item")
  395. .Select(entry =>
  396. {
  397. var monsterContentName = (string)entry.Element("ContentName");
  398. var monsterCount = (int?)entry.Element("Count") ?? 1;
  399. var monster = Monster.Load(Path.Combine("Characters", "Monsters", monsterContentName), contentManager);
  400. return new QuestRequirement<Monster>
  401. {
  402. ContentName = monsterContentName,
  403. Count = monsterCount,
  404. Content = monster
  405. };
  406. }).ToList() ?? new List<QuestRequirement<Monster>>(),
  407. FixedCombatEntries = questElement.Element("FixedCombatEntries")?.Elements("Item")
  408. .Select(item =>
  409. {
  410. var contentName = (string)item.Element("ContentName");
  411. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  412. var mapPosition = new Point(
  413. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  414. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  415. // Load the fixed combat asset XML using contentName
  416. var fixedCombat = FixedCombat.Load(Path.Combine("Maps", "FixedCombats", contentName), contentManager);
  417. AnimatingSprite animatingSprite = null;
  418. if (fixedCombat.Entries.Count > 0)
  419. {
  420. animatingSprite = fixedCombat.Entries[0].Content.MapSprite.Clone() as AnimatingSprite;
  421. }
  422. return new WorldEntry<FixedCombat>
  423. {
  424. ContentName = contentName,
  425. Content = fixedCombat,
  426. Direction = direction,
  427. MapPosition = mapPosition,
  428. MapSprite = animatingSprite,
  429. };
  430. }).ToList() ?? new List<WorldEntry<FixedCombat>>(),
  431. ChestEntries = questElement.Element("ChestEntries")?
  432. .Elements("Item")
  433. .Select(chestRequirement =>
  434. {
  435. var contentName = (string)chestRequirement.Element("ContentName");
  436. var direction = Enum.TryParse<Direction>((string)chestRequirement.Element("Direction"), out var dir) ? dir : default;
  437. var mapPosition = new Point(
  438. int.Parse(chestRequirement.Element("MapPosition").Value.Split(' ')[0]),
  439. int.Parse(chestRequirement.Element("MapPosition").Value.Split(' ')[1]));
  440. var mapContentName = (string)chestRequirement.Element("MapContentName");
  441. var count = (int?)chestRequirement.Element("Count") ?? 1;
  442. // Load the QuestNpc asset XML using contentName
  443. var chestAsset = XmlHelper.GetAssetElementFromXML(Path.Combine("Maps", "Chests", contentName));
  444. var chest = Chest.Load(chestAsset, contentManager);
  445. return new WorldEntry<Chest>
  446. {
  447. ContentName = contentName,
  448. Content = chest,
  449. Count = count,
  450. Direction = direction,
  451. MapContentName = mapContentName,
  452. MapPosition = mapPosition,
  453. MapSprite = null,
  454. };
  455. }).ToList() ?? new List<WorldEntry<Chest>>(),
  456. };
  457. // Load the gear rewards
  458. foreach (var gearContentName in quest.GearRewardContentNames)
  459. {
  460. var gear = Equipment.Load(Path.Combine("Gear", gearContentName), contentManager);
  461. quest.GearRewards.Add(gear);
  462. }
  463. return quest;
  464. }
  465. }
  466. }