Map.cs 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. //-----------------------------------------------------------------------------
  2. // Map.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using Microsoft.Xna.Framework;
  8. using Microsoft.Xna.Framework.Content;
  9. using Microsoft.Xna.Framework.Graphics;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.IO;
  13. using System.Linq;
  14. namespace RolePlaying.Data
  15. {
  16. /// <summary>
  17. /// One section of the world, and all of the data in it.
  18. /// </summary>
  19. public class Map : ContentObject
  20. #if WINDOWS
  21. , ICloneable
  22. #endif
  23. {
  24. /// <summary>
  25. /// The name of this section of the world.
  26. /// </summary>
  27. private string name;
  28. /// <summary>
  29. /// The name of this section of the world.
  30. /// </summary>
  31. public string Name
  32. {
  33. get { return name; }
  34. set { name = value; }
  35. }
  36. /// <summary>
  37. /// The dimensions of the map, in tiles.
  38. /// </summary>
  39. private Point mapDimensions;
  40. /// <summary>
  41. /// The dimensions of the map, in tiles.
  42. /// </summary>
  43. public Point MapDimensions
  44. {
  45. get { return mapDimensions; }
  46. set { mapDimensions = value; }
  47. }
  48. /// <summary>
  49. /// The size of the tiles in this map, in pixels.
  50. /// </summary>
  51. private Point tileSize;
  52. /// <summary>
  53. /// The size of the tiles in this map, in pixels.
  54. /// </summary>
  55. public Point TileSize
  56. {
  57. get { return tileSize; }
  58. set { tileSize = value; }
  59. }
  60. /// <summary>
  61. /// The number of tiles in a row of the map texture.
  62. /// </summary>
  63. /// <remarks>
  64. /// Used to determine the source rectangle from the map layer value.
  65. /// </remarks>
  66. private int tilesPerRow;
  67. /// <summary>
  68. /// The number of tiles in a row of the map texture.
  69. /// </summary>
  70. /// <remarks>
  71. /// Used to determine the source rectangle from the map layer value.
  72. /// </remarks>
  73. [ContentSerializerIgnore]
  74. public int TilesPerRow
  75. {
  76. get { return tilesPerRow; }
  77. set { tilesPerRow = value; }
  78. }
  79. /// <summary>
  80. /// A valid spawn position for this map.
  81. /// </summary>
  82. private Point spawnMapPosition;
  83. /// <summary>
  84. /// A valid spawn position for this map.
  85. /// </summary>
  86. public Point SpawnMapPosition
  87. {
  88. get { return spawnMapPosition; }
  89. set { spawnMapPosition = value; }
  90. }
  91. /// <summary>
  92. /// The content name of the texture that contains the tiles for this map.
  93. /// </summary>
  94. private string textureName;
  95. /// <summary>
  96. /// The content name of the texture that contains the tiles for this map.
  97. /// </summary>
  98. public string TextureName
  99. {
  100. get { return textureName; }
  101. set { textureName = value; }
  102. }
  103. /// <summary>
  104. /// The texture that contains the tiles for this map.
  105. /// </summary>
  106. private Texture2D texture;
  107. /// <summary>
  108. /// The texture that contains the tiles for this map.
  109. /// </summary>
  110. [ContentSerializerIgnore]
  111. public Texture2D Texture
  112. {
  113. get { return texture; }
  114. set { texture = value; }
  115. }
  116. /// <summary>
  117. /// The content name of the texture that contains the background for combats
  118. /// that occur while traveling on this map.
  119. /// </summary>
  120. private string combatTextureName;
  121. /// <summary>
  122. /// The content name of the texture that contains the background for combats
  123. /// that occur while traveling on this map.
  124. /// </summary>
  125. public string CombatTextureName
  126. {
  127. get { return combatTextureName; }
  128. set { combatTextureName = value; }
  129. }
  130. /// <summary>
  131. /// The texture that contains the background for combats
  132. /// that occur while traveling on this map.
  133. /// </summary>
  134. private Texture2D combatTexture;
  135. /// <summary>
  136. /// The texture that contains the background for combats
  137. /// that occur while traveling on this map.
  138. /// </summary>
  139. [ContentSerializerIgnore]
  140. public Texture2D CombatTexture
  141. {
  142. get { return combatTexture; }
  143. set { combatTexture = value; }
  144. }
  145. /// <summary>
  146. /// The name of the music cue for this map.
  147. /// </summary>
  148. private string musicCueName;
  149. /// <summary>
  150. /// The name of the music cue for this map.
  151. /// </summary>
  152. public string MusicCueName
  153. {
  154. get { return musicCueName; }
  155. set { musicCueName = value; }
  156. }
  157. /// <summary>
  158. /// The name of the music cue for combats that occur while traveling on this map.
  159. /// </summary>
  160. private string combatMusicCueName;
  161. /// <summary>
  162. /// The name of the music cue for combats that occur while traveling on this map.
  163. /// </summary>
  164. public string CombatMusicCueName
  165. {
  166. get { return combatMusicCueName; }
  167. set { combatMusicCueName = value; }
  168. }
  169. /// <summary>
  170. /// Spatial array for the ground tiles for this map.
  171. /// </summary>
  172. private int[] baseLayer;
  173. /// <summary>
  174. /// Spatial array for the ground tiles for this map.
  175. /// </summary>
  176. public int[] BaseLayer
  177. {
  178. get { return baseLayer; }
  179. set { baseLayer = value; }
  180. }
  181. /// <summary>
  182. /// Retrieves the base layer value for the given map position.
  183. /// </summary>
  184. public int GetBaseLayerValue(Point mapPosition)
  185. {
  186. // check the parameter
  187. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  188. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  189. {
  190. throw new ArgumentOutOfRangeException("mapPosition");
  191. }
  192. return baseLayer[mapPosition.Y * mapDimensions.X + mapPosition.X];
  193. }
  194. /// <summary>
  195. /// Retrieves the source rectangle for the tile in the given position
  196. /// in the base layer.
  197. /// </summary>
  198. /// <remarks>This method allows out-of-bound (blocked) positions.</remarks>
  199. public Rectangle GetBaseLayerSourceRectangle(Point mapPosition)
  200. {
  201. // check the parameter, but out-of-bounds is nonfatal
  202. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  203. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  204. {
  205. return Rectangle.Empty;
  206. }
  207. int baseLayerValue = GetBaseLayerValue(mapPosition);
  208. if (baseLayerValue < 0)
  209. {
  210. return Rectangle.Empty;
  211. }
  212. return new Rectangle(
  213. (baseLayerValue % tilesPerRow) * tileSize.X,
  214. (baseLayerValue / tilesPerRow) * tileSize.Y,
  215. tileSize.X, tileSize.Y);
  216. }
  217. /// <summary>
  218. /// Spatial array for the fringe tiles for this map.
  219. /// </summary>
  220. private int[] fringeLayer;
  221. /// <summary>
  222. /// Spatial array for the fringe tiles for this map.
  223. /// </summary>
  224. public int[] FringeLayer
  225. {
  226. get { return fringeLayer; }
  227. set { fringeLayer = value; }
  228. }
  229. /// <summary>
  230. /// Retrieves the fringe layer value for the given map position.
  231. /// </summary>
  232. public int GetFringeLayerValue(Point mapPosition)
  233. {
  234. // check the parameter
  235. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  236. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  237. {
  238. throw new ArgumentOutOfRangeException("mapPosition");
  239. }
  240. return fringeLayer[mapPosition.Y * mapDimensions.X + mapPosition.X];
  241. }
  242. /// <summary>
  243. /// Retrieves the source rectangle for the tile in the given position
  244. /// in the fringe layer.
  245. /// </summary>
  246. /// <remarks>This method allows out-of-bound (blocked) positions.</remarks>
  247. public Rectangle GetFringeLayerSourceRectangle(Point mapPosition)
  248. {
  249. // check the parameter, but out-of-bounds is nonfatal
  250. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  251. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  252. {
  253. return Rectangle.Empty;
  254. }
  255. int fringeLayerValue = GetFringeLayerValue(mapPosition);
  256. if (fringeLayerValue < 0)
  257. {
  258. return Rectangle.Empty;
  259. }
  260. return new Rectangle(
  261. (fringeLayerValue % tilesPerRow) * tileSize.X,
  262. (fringeLayerValue / tilesPerRow) * tileSize.Y,
  263. tileSize.X, tileSize.Y);
  264. }
  265. /// <summary>
  266. /// Spatial array for the object images on this map.
  267. /// </summary>
  268. private int[] objectLayer;
  269. /// <summary>
  270. /// Spatial array for the object images on this map.
  271. /// </summary>
  272. public int[] ObjectLayer
  273. {
  274. get { return objectLayer; }
  275. set { objectLayer = value; }
  276. }
  277. /// <summary>
  278. /// Retrieves the object layer value for the given map position.
  279. /// </summary>
  280. public int GetObjectLayerValue(Point mapPosition)
  281. {
  282. // check the parameter
  283. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  284. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  285. {
  286. throw new ArgumentOutOfRangeException("mapPosition");
  287. }
  288. return objectLayer[mapPosition.Y * mapDimensions.X + mapPosition.X];
  289. }
  290. /// <summary>
  291. /// Retrieves the source rectangle for the tile in the given position
  292. /// in the object layer.
  293. /// </summary>
  294. /// <remarks>This method allows out-of-bound (blocked) positions.</remarks>
  295. public Rectangle GetObjectLayerSourceRectangle(Point mapPosition)
  296. {
  297. // check the parameter, but out-of-bounds is nonfatal
  298. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  299. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  300. {
  301. return Rectangle.Empty;
  302. }
  303. int objectLayerValue = GetObjectLayerValue(mapPosition);
  304. if (objectLayerValue < 0)
  305. {
  306. return Rectangle.Empty;
  307. }
  308. return new Rectangle(
  309. (objectLayerValue % tilesPerRow) * tileSize.X,
  310. (objectLayerValue / tilesPerRow) * tileSize.Y,
  311. tileSize.X, tileSize.Y);
  312. }
  313. /// <summary>
  314. /// Spatial array for the collision properties of this map.
  315. /// </summary>
  316. private int[] collisionLayer;
  317. /// <summary>
  318. /// Spatial array for the collision properties of this map.
  319. /// </summary>
  320. public int[] CollisionLayer
  321. {
  322. get { return collisionLayer; }
  323. set { collisionLayer = value; }
  324. }
  325. /// <summary>
  326. /// Retrieves the collision layer value for the given map position.
  327. /// </summary>
  328. public int GetCollisionLayerValue(Point mapPosition)
  329. {
  330. // check the parameter
  331. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  332. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  333. {
  334. throw new ArgumentOutOfRangeException("mapPosition");
  335. }
  336. return collisionLayer[mapPosition.Y * mapDimensions.X + mapPosition.X];
  337. }
  338. /// <summary>
  339. /// Returns true if the given map position is blocked.
  340. /// </summary>
  341. /// <remarks>This method allows out-of-bound (blocked) positions.</remarks>
  342. public bool IsBlocked(Point mapPosition)
  343. {
  344. // check the parameter, but out-of-bounds is nonfatal
  345. if ((mapPosition.X < 0) || (mapPosition.X >= mapDimensions.X) ||
  346. (mapPosition.Y < 0) || (mapPosition.Y >= mapDimensions.Y))
  347. {
  348. return true;
  349. }
  350. return (GetCollisionLayerValue(mapPosition) != 0);
  351. }
  352. /// <summary>
  353. /// Portals to other maps.
  354. /// </summary>
  355. private List<Portal> portals = new List<Portal>();
  356. /// <summary>
  357. /// Portals to other maps.
  358. /// </summary>
  359. public List<Portal> Portals
  360. {
  361. get { return portals; }
  362. set { portals = value; }
  363. }
  364. /// <summary>
  365. /// The content names and positions of the portals on this map.
  366. /// </summary>
  367. private List<MapEntry<Portal>> portalEntries =
  368. new List<MapEntry<Portal>>();
  369. /// <summary>
  370. /// The content names and positions of the portals on this map.
  371. /// </summary>
  372. public List<MapEntry<Portal>> PortalEntries
  373. {
  374. get { return portalEntries; }
  375. set { portalEntries = value; }
  376. }
  377. /// <summary>
  378. /// Find a portal on this map based on the given portal name.
  379. /// </summary>
  380. public MapEntry<Portal> FindPortal(string name)
  381. {
  382. // check the parameter
  383. if (String.IsNullOrEmpty(name))
  384. {
  385. throw new ArgumentNullException("name");
  386. }
  387. return portalEntries.Find(delegate (MapEntry<Portal> portalEntry)
  388. {
  389. return (portalEntry.ContentName == name);
  390. });
  391. }
  392. /// <summary>
  393. /// The content names and positions of the treasure chests on this map.
  394. /// </summary>
  395. private List<MapEntry<Chest>> chestEntries =
  396. new List<MapEntry<Chest>>();
  397. /// <summary>
  398. /// The content names and positions of the treasure chests on this map.
  399. /// </summary>
  400. public List<MapEntry<Chest>> ChestEntries
  401. {
  402. get { return chestEntries; }
  403. set { chestEntries = value; }
  404. }
  405. /// <summary>
  406. /// The content name, positions, and orientations of the
  407. /// fixed combat encounters on this map.
  408. /// </summary>
  409. private List<MapEntry<FixedCombat>> fixedCombatEntries =
  410. new List<MapEntry<FixedCombat>>();
  411. /// <summary>
  412. /// The content name, positions, and orientations of the
  413. /// fixed combat encounters on this map.
  414. /// </summary>
  415. public List<MapEntry<FixedCombat>> FixedCombatEntries
  416. {
  417. get { return fixedCombatEntries; }
  418. set { fixedCombatEntries = value; }
  419. }
  420. /// <summary>
  421. /// The random combat definition for this map.
  422. /// </summary>
  423. private RandomCombat randomCombat;
  424. /// <summary>
  425. /// The random combat definition for this map.
  426. /// </summary>
  427. public RandomCombat RandomCombat
  428. {
  429. get { return randomCombat; }
  430. set { randomCombat = value; }
  431. }
  432. /// <summary>
  433. /// The content names, positions, and orientations of quest Npcs on this map.
  434. /// </summary>
  435. private List<MapEntry<QuestNpc>> questNpcEntries =
  436. new List<MapEntry<QuestNpc>>();
  437. /// <summary>
  438. /// The content names, positions, and orientations of quest Npcs on this map.
  439. /// </summary>
  440. public List<MapEntry<QuestNpc>> QuestNpcEntries
  441. {
  442. get { return questNpcEntries; }
  443. set { questNpcEntries = value; }
  444. }
  445. /// <summary>
  446. /// The content names, positions, and orientations of player Npcs on this map.
  447. /// </summary>
  448. private List<MapEntry<Player>> playerNpcEntries =
  449. new List<MapEntry<Player>>();
  450. /// <summary>
  451. /// The content names, positions, and orientations of player Npcs on this map.
  452. /// </summary>
  453. public List<MapEntry<Player>> PlayerNpcEntries
  454. {
  455. get { return playerNpcEntries; }
  456. set { playerNpcEntries = value; }
  457. }
  458. /// <summary>
  459. /// The content names, positions, and orientations of the inns on this map.
  460. /// </summary>
  461. private List<MapEntry<Inn>> innEntries =
  462. new List<MapEntry<Inn>>();
  463. /// <summary>
  464. /// The content names, positions, and orientations of the inns on this map.
  465. /// </summary>
  466. public List<MapEntry<Inn>> InnEntries
  467. {
  468. get { return innEntries; }
  469. set { innEntries = value; }
  470. }
  471. /// <summary>
  472. /// The content names, positions, and orientations of the stores on this map.
  473. /// </summary>
  474. private List<MapEntry<Store>> storeEntries =
  475. new List<MapEntry<Store>>();
  476. /// <summary>
  477. /// The content names, positions, and orientations of the stores on this map.
  478. /// </summary>
  479. public List<MapEntry<Store>> StoreEntries
  480. {
  481. get { return storeEntries; }
  482. set { storeEntries = value; }
  483. }
  484. public object Clone()
  485. {
  486. Map map = new Map();
  487. map.AssetName = AssetName;
  488. map.baseLayer = BaseLayer.Clone() as int[];
  489. foreach (MapEntry<Chest> chestEntry in chestEntries)
  490. {
  491. MapEntry<Chest> mapEntry = new MapEntry<Chest>();
  492. mapEntry.Content = chestEntry.Content.Clone() as Chest;
  493. mapEntry.ContentName = chestEntry.ContentName;
  494. mapEntry.Count = chestEntry.Count;
  495. mapEntry.Direction = chestEntry.Direction;
  496. mapEntry.MapPosition = chestEntry.MapPosition;
  497. map.chestEntries.Add(mapEntry);
  498. }
  499. map.chestEntries.AddRange(ChestEntries);
  500. map.collisionLayer = CollisionLayer.Clone() as int[];
  501. map.combatMusicCueName = CombatMusicCueName;
  502. map.combatTexture = CombatTexture;
  503. map.combatTextureName = CombatTextureName;
  504. map.fixedCombatEntries.AddRange(FixedCombatEntries);
  505. map.fringeLayer = FringeLayer.Clone() as int[];
  506. map.innEntries.AddRange(InnEntries);
  507. map.mapDimensions = MapDimensions;
  508. map.musicCueName = MusicCueName;
  509. map.name = Name;
  510. map.objectLayer = ObjectLayer.Clone() as int[];
  511. map.playerNpcEntries.AddRange(PlayerNpcEntries);
  512. map.portals.AddRange(Portals);
  513. map.portalEntries.AddRange(PortalEntries);
  514. map.questNpcEntries.AddRange(QuestNpcEntries);
  515. map.randomCombat = new RandomCombat();
  516. map.randomCombat.CombatProbability = RandomCombat.CombatProbability;
  517. map.randomCombat.Entries.AddRange(RandomCombat.Entries);
  518. map.randomCombat.FleeProbability = RandomCombat.FleeProbability;
  519. map.randomCombat.MonsterCountRange = RandomCombat.MonsterCountRange;
  520. map.spawnMapPosition = SpawnMapPosition;
  521. map.storeEntries.AddRange(StoreEntries);
  522. map.texture = Texture;
  523. map.textureName = TextureName;
  524. map.tileSize = TileSize;
  525. map.tilesPerRow = tilesPerRow;
  526. return map;
  527. }
  528. public static Map Load(string mapContentName, ContentManager contentManager)
  529. {
  530. var asset = XmlHelper.GetAssetElementFromXML(mapContentName);
  531. var map = new Map
  532. {
  533. AssetName = mapContentName,
  534. Name = asset.Element("Name").Value,
  535. MapDimensions = new Point(
  536. int.Parse(asset.Element("MapDimensions").Value.Split(' ')[0]),
  537. int.Parse(asset.Element("MapDimensions").Value.Split(' ')[1])), // e.g. [20, 23]
  538. TileSize = new Point(
  539. int.Parse(asset.Element("TileSize").Value.Split(' ')[0]),
  540. int.Parse(asset.Element("TileSize").Value.Split(' ')[1])), // e.g. [64, 64]
  541. SpawnMapPosition = new Point(
  542. int.Parse(asset.Element("SpawnMapPosition").Value.Split(' ')[0]),
  543. int.Parse(asset.Element("SpawnMapPosition").Value.Split(' ')[1])), // e.g. [9, 7]
  544. TextureName = (string)asset.Element("TextureName"),
  545. Texture = contentManager.Load<Texture2D>(Path.Combine("Textures", "Maps", "NonCombat", (string)asset.Element("TextureName"))),
  546. CombatTextureName = (string)asset.Element("CombatTextureName"),
  547. CombatTexture = contentManager.Load<Texture2D>(Path.Combine("Textures", "Maps", "Combat", (string)asset.Element("CombatTextureName"))),
  548. MusicCueName = (string)asset.Element("MusicCueName"),
  549. CombatMusicCueName = (string)asset.Element("CombatMusicCueName"),
  550. BaseLayer = asset.Element("BaseLayer").Value
  551. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  552. .Select(int.Parse)
  553. .ToArray(),
  554. FringeLayer = asset.Element("FringeLayer").Value
  555. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  556. .Select(int.Parse)
  557. .ToArray(),
  558. ObjectLayer = asset.Element("ObjectLayer").Value
  559. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  560. .Select(int.Parse)
  561. .ToArray(),
  562. CollisionLayer = asset.Element("CollisionLayer").Value
  563. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  564. .Select(int.Parse)
  565. .ToArray(),
  566. Portals = asset.Element("Portals")?.Elements("Item")
  567. .Select(item => new Portal
  568. {
  569. Name = (string)item.Element("Name"),
  570. LandingMapPosition = new Point(
  571. int.Parse(item.Element("LandingMapPosition").Value.Split(' ')[0]),
  572. int.Parse(item.Element("LandingMapPosition").Value.Split(' ')[1])),
  573. DestinationMapContentName = (string)item.Element("DestinationMapContentName"),
  574. DestinationMapPortalName = (string)item.Element("DestinationMapPortalName")
  575. }).ToList(),
  576. PortalEntries = asset.Element("PortalEntries")?.Elements("Item")
  577. .Select(item =>
  578. {
  579. var contentName = (string)item.Element("ContentName");
  580. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  581. var mapPosition = new Point(
  582. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  583. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  584. // Load the QuestNpc asset XML using contentName
  585. var portalItem = asset.Element("Portals")?.Elements("Item").FirstOrDefault(p => (string)p.Element("Name") == contentName);
  586. var portal = new Portal
  587. {
  588. Name = (string)portalItem.Element("Name"),
  589. LandingMapPosition = new Point(
  590. int.Parse(portalItem.Element("LandingMapPosition").Value.Split(' ')[0]),
  591. int.Parse(portalItem.Element("LandingMapPosition").Value.Split(' ')[1])),
  592. DestinationMapContentName = (string)portalItem.Element("DestinationMapContentName"),
  593. DestinationMapPortalName = (string)portalItem.Element("DestinationMapPortalName")
  594. };
  595. return new MapEntry<Portal>
  596. {
  597. ContentName = contentName,
  598. Content = portal,
  599. Direction = direction,
  600. MapPosition = mapPosition
  601. };
  602. }).ToList(),
  603. ChestEntries = asset.Element("ChestEntries")?.Elements("Item")
  604. .Select(item =>
  605. {
  606. var contentName = (string)item.Element("ContentName");
  607. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  608. var mapPosition = new Point(
  609. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  610. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  611. // Load the QuestNpc asset XML using contentName
  612. var chestAsset = XmlHelper.GetAssetElementFromXML(Path.Combine("Maps", "Chests", contentName));
  613. var chest = Chest.Load(chestAsset, contentManager);
  614. return new MapEntry<Chest>
  615. {
  616. ContentName = contentName,
  617. Content = chest,
  618. Direction = direction,
  619. MapPosition = mapPosition
  620. };
  621. }).ToList(),
  622. FixedCombatEntries = asset.Element("FixedCombatEntries")?.Elements("Item")
  623. .Select(item =>
  624. {
  625. var contentName = (string)item.Element("ContentName");
  626. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  627. var mapPosition = new Point(
  628. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  629. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  630. // Load the fixed combat asset XML using contentName
  631. var fixedCombat = FixedCombat.Load(Path.Combine("Maps", "FixedCombats", contentName), contentManager);
  632. AnimatingSprite animatingSprite = null;
  633. if (fixedCombat.Entries.Count > 0)
  634. {
  635. animatingSprite = fixedCombat.Entries[0].Content.MapSprite.Clone() as AnimatingSprite;
  636. }
  637. return new MapEntry<FixedCombat>
  638. {
  639. ContentName = contentName,
  640. Content = fixedCombat,
  641. Direction = direction,
  642. MapPosition = mapPosition,
  643. MapSprite = animatingSprite,
  644. };
  645. }).ToList(),
  646. PlayerNpcEntries = asset.Element("PlayerNpcEntries")?.Elements("Item")
  647. .Select(item =>
  648. {
  649. var contentName = (string)item.Element("ContentName");
  650. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  651. var mapPosition = new Point(
  652. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  653. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  654. // Load the PlayerNpc asset XML using contentName
  655. var playerNpc = Player.Load(Path.Combine("Characters", "Players", contentName), contentManager);
  656. return new MapEntry<Player>
  657. {
  658. ContentName = contentName,
  659. Content = playerNpc,
  660. Direction = direction,
  661. MapPosition = mapPosition
  662. };
  663. }).ToList(),
  664. QuestNpcEntries = asset.Element("QuestNpcEntries")?.Elements("Item")
  665. .Select(item =>
  666. {
  667. var contentName = (string)item.Element("ContentName");
  668. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  669. var mapPosition = new Point(
  670. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  671. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  672. // Load the QuestNpc asset XML using contentName
  673. var questNpc = QuestNpc.Load(Path.Combine("Characters", "QuestNPCs", contentName), contentManager);
  674. return new MapEntry<QuestNpc>
  675. {
  676. ContentName = contentName,
  677. Content = questNpc,
  678. Direction = direction,
  679. MapPosition = mapPosition
  680. };
  681. }).ToList(),
  682. InnEntries = asset.Element("InnEntries")?.Elements("Item")
  683. .Select(item =>
  684. {
  685. var contentName = (string)item.Element("ContentName");
  686. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  687. var mapPosition = new Point(
  688. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  689. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  690. // Load the Inn asset XML using contentName
  691. var inn = Inn.Load(Path.Combine("Maps", "Inns", contentName), contentManager);
  692. return new MapEntry<Inn>
  693. {
  694. ContentName = contentName,
  695. Content = inn,
  696. Direction = direction,
  697. MapPosition = mapPosition
  698. };
  699. }).ToList(),
  700. StoreEntries = asset.Element("StoreEntries")?.Elements("Item")
  701. .Select(item =>
  702. {
  703. var contentName = (string)item.Element("ContentName");
  704. var direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default;
  705. var mapPosition = new Point(
  706. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  707. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]));
  708. // Load the Store asset XML using contentName
  709. var store = Store.Load(Path.Combine("Maps", "Stores", contentName), contentManager);
  710. return new MapEntry<Store>
  711. {
  712. ContentName = contentName,
  713. Content = store,
  714. Direction = direction,
  715. MapPosition = mapPosition
  716. };
  717. }).ToList(),
  718. };
  719. var randomCombatElement = asset.Element("RandomCombat");
  720. if (randomCombatElement != null)
  721. {
  722. map.RandomCombat = new RandomCombat
  723. {
  724. CombatProbability = (int?)randomCombatElement.Element("CombatProbability") ?? 0,
  725. FleeProbability = (int?)randomCombatElement.Element("FleeProbability") ?? 0,
  726. MonsterCountRange = new Int32Range
  727. {
  728. Minimum = (int?)randomCombatElement.Element("MonsterCountRange")?.Element("Minimum") ?? 0,
  729. Maximum = (int?)randomCombatElement.Element("MonsterCountRange")?.Element("Maximum") ?? 0
  730. },
  731. Entries = randomCombatElement.Element("Entries")?.Elements("Item")
  732. .Select(item => new WeightedContentEntry<Monster>
  733. {
  734. ContentName = (string)item.Element("ContentName"),
  735. Count = (int?)item.Element("Count") ?? 0,
  736. Weight = (int?)item.Element("Weight") ?? 0
  737. }).ToList()
  738. };
  739. }
  740. map.TilesPerRow = map.Texture.Width / map.TileSize.X;
  741. return map;
  742. }
  743. /// <summary>
  744. /// Read a Map object from the content pipeline.
  745. /// </summary>
  746. public class MapReader : ContentTypeReader<Map>
  747. {
  748. protected override Map Read(ContentReader input, Map existingInstance)
  749. {
  750. Map map = existingInstance;
  751. if (map == null)
  752. {
  753. map = new Map();
  754. }
  755. map.AssetName = input.AssetName;
  756. map.Name = input.ReadString();
  757. map.MapDimensions = input.ReadObject<Point>();
  758. map.TileSize = input.ReadObject<Point>();
  759. map.SpawnMapPosition = input.ReadObject<Point>();
  760. map.TextureName = input.ReadString();
  761. map.texture = input.ContentManager.Load<Texture2D>(Path.Combine("Textures", "Maps", "NonCombat", map.TextureName));
  762. map.tilesPerRow = map.texture.Width / map.TileSize.X;
  763. map.CombatTextureName = input.ReadString();
  764. map.combatTexture = input.ContentManager.Load<Texture2D>(Path.Combine("Textures", "Maps", "Combat", map.CombatTextureName));
  765. map.MusicCueName = input.ReadString();
  766. map.CombatMusicCueName = input.ReadString();
  767. map.BaseLayer = input.ReadObject<int[]>();
  768. map.FringeLayer = input.ReadObject<int[]>();
  769. map.ObjectLayer = input.ReadObject<int[]>();
  770. map.CollisionLayer = input.ReadObject<int[]>();
  771. map.Portals.AddRange(input.ReadObject<List<Portal>>());
  772. map.PortalEntries.AddRange(
  773. input.ReadObject<List<MapEntry<Portal>>>());
  774. foreach (MapEntry<Portal> portalEntry in map.PortalEntries)
  775. {
  776. portalEntry.Content = map.Portals.Find(delegate (Portal portal)
  777. {
  778. return (portal.Name == portalEntry.ContentName);
  779. });
  780. }
  781. map.ChestEntries.AddRange(input.ReadObject<List<MapEntry<Chest>>>());
  782. foreach (MapEntry<Chest> chestEntry in map.chestEntries)
  783. {
  784. chestEntry.Content = input.ContentManager.Load<Chest>(Path.Combine("Maps", "Chests", chestEntry.ContentName)).Clone() as Chest;
  785. }
  786. // load the fixed combat entries
  787. Random random = new Random();
  788. map.FixedCombatEntries.AddRange(
  789. input.ReadObject<List<MapEntry<FixedCombat>>>());
  790. foreach (MapEntry<FixedCombat> fixedCombatEntry in
  791. map.fixedCombatEntries)
  792. {
  793. fixedCombatEntry.Content =
  794. input.ContentManager.Load<FixedCombat>(Path.Combine("Maps", "FixedCombats", fixedCombatEntry.ContentName));
  795. // clone the map sprite in the entry, as there may be many entries
  796. // per FixedCombat
  797. fixedCombatEntry.MapSprite =
  798. fixedCombatEntry.Content.Entries[0].Content.MapSprite.Clone()
  799. as AnimatingSprite;
  800. // play the idle animation
  801. fixedCombatEntry.MapSprite.PlayAnimation("Idle",
  802. fixedCombatEntry.Direction);
  803. // advance in a random amount so the animations aren't synchronized
  804. fixedCombatEntry.MapSprite.UpdateAnimation(
  805. 4f * (float)random.NextDouble());
  806. }
  807. map.RandomCombat = input.ReadObject<RandomCombat>();
  808. map.QuestNpcEntries.AddRange(
  809. input.ReadObject<List<MapEntry<QuestNpc>>>());
  810. foreach (MapEntry<QuestNpc> questNpcEntry in
  811. map.questNpcEntries)
  812. {
  813. questNpcEntry.Content = input.ContentManager.Load<QuestNpc>(Path.Combine("Characters", "QuestNpcs", questNpcEntry.ContentName));
  814. questNpcEntry.Content.MapPosition = questNpcEntry.MapPosition;
  815. questNpcEntry.Content.Direction = questNpcEntry.Direction;
  816. }
  817. map.PlayerNpcEntries.AddRange(
  818. input.ReadObject<List<MapEntry<Player>>>());
  819. foreach (MapEntry<Player> playerNpcEntry in
  820. map.playerNpcEntries)
  821. {
  822. playerNpcEntry.Content = input.ContentManager.Load<Player>(Path.Combine("Characters", "Players", playerNpcEntry.ContentName)).Clone() as Player;
  823. playerNpcEntry.Content.MapPosition = playerNpcEntry.MapPosition;
  824. playerNpcEntry.Content.Direction = playerNpcEntry.Direction;
  825. }
  826. map.InnEntries.AddRange(
  827. input.ReadObject<List<MapEntry<Inn>>>());
  828. foreach (MapEntry<Inn> innEntry in
  829. map.innEntries)
  830. {
  831. innEntry.Content = input.ContentManager.Load<Inn>(Path.Combine("Maps", "Inns", innEntry.ContentName));
  832. }
  833. map.StoreEntries.AddRange(
  834. input.ReadObject<List<MapEntry<Store>>>());
  835. foreach (MapEntry<Store> storeEntry in
  836. map.storeEntries)
  837. {
  838. storeEntry.Content = input.ContentManager.Load<Store>(Path.Combine("Maps", "Stores", storeEntry.ContentName));
  839. }
  840. return map;
  841. }
  842. }
  843. }
  844. }