Map.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  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 content)
  529. {
  530. var asset = XmlHelper.GetAssetElementFromXML(mapContentName);
  531. var map = new Map
  532. {
  533. Name = asset.Element("Name").Value,
  534. MapDimensions = new Point(
  535. int.Parse(asset.Element("MapDimensions").Value.Split(' ')[0]),
  536. int.Parse(asset.Element("MapDimensions").Value.Split(' ')[1])), // e.g. [20, 23]
  537. TileSize = new Point(
  538. int.Parse(asset.Element("TileSize").Value.Split(' ')[0]),
  539. int.Parse(asset.Element("TileSize").Value.Split(' ')[1])), // e.g. [64, 64]
  540. SpawnMapPosition = new Point(
  541. int.Parse(asset.Element("SpawnMapPosition").Value.Split(' ')[0]),
  542. int.Parse(asset.Element("SpawnMapPosition").Value.Split(' ')[1])), // e.g. [9, 7]
  543. TextureName = (string)asset.Element("TextureName"),
  544. Texture = content.Load<Texture2D>(
  545. Path.Combine(@"Textures\Maps\NonCombat", (string)asset.Element("TextureName"))),
  546. CombatTextureName = (string)asset.Element("CombatTextureName"),
  547. CombatTexture = content.Load<Texture2D>(
  548. Path.Combine(@"Textures\Maps\Combat", (string)asset.Element("CombatTextureName"))),
  549. MusicCueName = (string)asset.Element("MusicCueName"),
  550. CombatMusicCueName = (string)asset.Element("CombatMusicCueName"),
  551. BaseLayer = asset.Element("BaseLayer").Value
  552. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  553. .Select(int.Parse)
  554. .ToArray(),
  555. FringeLayer = asset.Element("FringeLayer").Value
  556. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  557. .Select(int.Parse)
  558. .ToArray(),
  559. ObjectLayer = asset.Element("ObjectLayer").Value
  560. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  561. .Select(int.Parse)
  562. .ToArray(),
  563. CollisionLayer = asset.Element("CollisionLayer").Value
  564. .Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
  565. .Select(int.Parse)
  566. .ToArray(),
  567. Portals = asset.Element("Portals")?.Elements("Item")
  568. .Select(item => new Portal
  569. {
  570. Name = (string)item.Element("Name"),
  571. LandingMapPosition = new Point(
  572. int.Parse(item.Element("LandingMapPosition").Value.Split(' ')[0]),
  573. int.Parse(item.Element("LandingMapPosition").Value.Split(' ')[1])),
  574. DestinationMapContentName = (string)item.Element("DestinationMapContentName"),
  575. DestinationMapPortalName = (string)item.Element("DestinationMapPortalName")
  576. }).ToList(),
  577. PortalEntries = asset.Element("PortalEntries")?.Elements("Item")
  578. .Select(item => new MapEntry<Portal>
  579. {
  580. ContentName = (string)item.Element("ContentName"),
  581. MapPosition = new Point(
  582. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  583. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]))
  584. }).ToList(),
  585. ChestEntries = asset.Element("ChestEntries")?.Elements("Item")
  586. .Select(item => new MapEntry<Chest>
  587. {
  588. ContentName = (string)item.Element("ContentName"),
  589. MapPosition = new Point(
  590. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  591. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]))
  592. }).ToList(),
  593. FixedCombatEntries = asset.Element("FixedCombatEntries")?.Elements("Item")
  594. .Select(item => new MapEntry<FixedCombat>
  595. {
  596. ContentName = (string)item.Element("ContentName"),
  597. MapPosition = new Point(
  598. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  599. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]))
  600. }).ToList(),
  601. PlayerNpcEntries = asset.Element("PlayerNpcEntries")?.Elements("Item")
  602. .Select(item => new MapEntry<Player>
  603. {
  604. ContentName = (string)item.Element("ContentName"),
  605. MapPosition = new Point(
  606. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  607. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]))
  608. }).ToList(),
  609. QuestNpcEntries = asset.Element("QuestNpcEntries")?.Elements("Item")
  610. .Select(item => new MapEntry<QuestNpc>
  611. {
  612. ContentName = (string)item.Element("ContentName"),
  613. Direction = Enum.TryParse<Direction>((string)item.Element("Direction"), out var dir) ? dir : default,
  614. MapPosition = new Point(
  615. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  616. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]))
  617. }).ToList(),
  618. InnEntries = asset.Element("InnEntries")?.Elements("Item")
  619. .Select(item => new MapEntry<Inn>
  620. {
  621. ContentName = (string)item.Element("ContentName"),
  622. MapPosition = new Point(
  623. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  624. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]))
  625. }).ToList(),
  626. StoreEntries = asset.Element("StoreEntries")?.Elements("Item")
  627. .Select(item => new MapEntry<Store>
  628. {
  629. ContentName = (string)item.Element("ContentName"),
  630. MapPosition = new Point(
  631. int.Parse(item.Element("MapPosition").Value.Split(' ')[0]),
  632. int.Parse(item.Element("MapPosition").Value.Split(' ')[1]))
  633. }).ToList(),
  634. };
  635. var randomCombatElement = asset.Element("RandomCombat");
  636. if (randomCombatElement != null)
  637. {
  638. map.RandomCombat = new RandomCombat
  639. {
  640. CombatProbability = (int?)randomCombatElement.Element("CombatProbability") ?? 0,
  641. FleeProbability = (int?)randomCombatElement.Element("FleeProbability") ?? 0,
  642. MonsterCountRange = new Int32Range
  643. {
  644. Minimum = (int?)randomCombatElement.Element("MonsterCountRange")?.Element("Minimum") ?? 0,
  645. Maximum = (int?)randomCombatElement.Element("MonsterCountRange")?.Element("Maximum") ?? 0
  646. },
  647. Entries = randomCombatElement.Element("Entries")?.Elements("Item")
  648. .Select(item => new WeightedContentEntry<Monster>
  649. {
  650. ContentName = (string)item.Element("ContentName"),
  651. Count = (int?)item.Element("Count") ?? 0,
  652. Weight = (int?)item.Element("Weight") ?? 0
  653. }).ToList()
  654. };
  655. }
  656. map.TilesPerRow = map.Texture.Width / map.TileSize.X;
  657. return map;
  658. }
  659. /// <summary>
  660. /// Read a Map object from the content pipeline.
  661. /// </summary>
  662. public class MapReader : ContentTypeReader<Map>
  663. {
  664. protected override Map Read(ContentReader input, Map existingInstance)
  665. {
  666. Map map = existingInstance;
  667. if (map == null)
  668. {
  669. map = new Map();
  670. }
  671. map.AssetName = input.AssetName;
  672. map.Name = input.ReadString();
  673. map.MapDimensions = input.ReadObject<Point>();
  674. map.TileSize = input.ReadObject<Point>();
  675. map.SpawnMapPosition = input.ReadObject<Point>();
  676. map.TextureName = input.ReadString();
  677. map.texture = input.ContentManager.Load<Texture2D>(
  678. System.IO.Path.Combine(@"Textures\Maps\NonCombat",
  679. map.TextureName));
  680. map.tilesPerRow = map.texture.Width / map.TileSize.X;
  681. map.CombatTextureName = input.ReadString();
  682. map.combatTexture = input.ContentManager.Load<Texture2D>(
  683. System.IO.Path.Combine(@"Textures\Maps\Combat",
  684. map.CombatTextureName));
  685. map.MusicCueName = input.ReadString();
  686. map.CombatMusicCueName = input.ReadString();
  687. map.BaseLayer = input.ReadObject<int[]>();
  688. map.FringeLayer = input.ReadObject<int[]>();
  689. map.ObjectLayer = input.ReadObject<int[]>();
  690. map.CollisionLayer = input.ReadObject<int[]>();
  691. map.Portals.AddRange(input.ReadObject<List<Portal>>());
  692. map.PortalEntries.AddRange(
  693. input.ReadObject<List<MapEntry<Portal>>>());
  694. foreach (MapEntry<Portal> portalEntry in map.PortalEntries)
  695. {
  696. portalEntry.Content = map.Portals.Find(delegate (Portal portal)
  697. {
  698. return (portal.Name == portalEntry.ContentName);
  699. });
  700. }
  701. map.ChestEntries.AddRange(
  702. input.ReadObject<List<MapEntry<Chest>>>());
  703. foreach (MapEntry<Chest> chestEntry in map.chestEntries)
  704. {
  705. chestEntry.Content = input.ContentManager.Load<Chest>(
  706. System.IO.Path.Combine(@"Maps\Chests",
  707. chestEntry.ContentName)).Clone() as Chest;
  708. }
  709. // load the fixed combat entries
  710. Random random = new Random();
  711. map.FixedCombatEntries.AddRange(
  712. input.ReadObject<List<MapEntry<FixedCombat>>>());
  713. foreach (MapEntry<FixedCombat> fixedCombatEntry in
  714. map.fixedCombatEntries)
  715. {
  716. fixedCombatEntry.Content =
  717. input.ContentManager.Load<FixedCombat>(
  718. System.IO.Path.Combine(@"Maps\FixedCombats",
  719. fixedCombatEntry.ContentName));
  720. // clone the map sprite in the entry, as there may be many entries
  721. // per FixedCombat
  722. fixedCombatEntry.MapSprite =
  723. fixedCombatEntry.Content.Entries[0].Content.MapSprite.Clone()
  724. as AnimatingSprite;
  725. // play the idle animation
  726. fixedCombatEntry.MapSprite.PlayAnimation("Idle",
  727. fixedCombatEntry.Direction);
  728. // advance in a random amount so the animations aren't synchronized
  729. fixedCombatEntry.MapSprite.UpdateAnimation(
  730. 4f * (float)random.NextDouble());
  731. }
  732. map.RandomCombat = input.ReadObject<RandomCombat>();
  733. map.QuestNpcEntries.AddRange(
  734. input.ReadObject<List<MapEntry<QuestNpc>>>());
  735. foreach (MapEntry<QuestNpc> questNpcEntry in
  736. map.questNpcEntries)
  737. {
  738. questNpcEntry.Content = input.ContentManager.Load<QuestNpc>(
  739. System.IO.Path.Combine(@"Characters\QuestNpcs",
  740. questNpcEntry.ContentName));
  741. questNpcEntry.Content.MapPosition = questNpcEntry.MapPosition;
  742. questNpcEntry.Content.Direction = questNpcEntry.Direction;
  743. }
  744. map.PlayerNpcEntries.AddRange(
  745. input.ReadObject<List<MapEntry<Player>>>());
  746. foreach (MapEntry<Player> playerNpcEntry in
  747. map.playerNpcEntries)
  748. {
  749. playerNpcEntry.Content = input.ContentManager.Load<Player>(
  750. System.IO.Path.Combine(@"Characters\Players",
  751. playerNpcEntry.ContentName)).Clone() as Player;
  752. playerNpcEntry.Content.MapPosition = playerNpcEntry.MapPosition;
  753. playerNpcEntry.Content.Direction = playerNpcEntry.Direction;
  754. }
  755. map.InnEntries.AddRange(
  756. input.ReadObject<List<MapEntry<Inn>>>());
  757. foreach (MapEntry<Inn> innEntry in
  758. map.innEntries)
  759. {
  760. innEntry.Content = input.ContentManager.Load<Inn>(
  761. System.IO.Path.Combine(@"Maps\Inns",
  762. innEntry.ContentName));
  763. }
  764. map.StoreEntries.AddRange(
  765. input.ReadObject<List<MapEntry<Store>>>());
  766. foreach (MapEntry<Store> storeEntry in
  767. map.storeEntries)
  768. {
  769. storeEntry.Content = input.ContentManager.Load<Store>(
  770. System.IO.Path.Combine(@"Maps\Stores",
  771. storeEntry.ContentName));
  772. }
  773. return map;
  774. }
  775. }
  776. }
  777. }