PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/WorldView/WorldMapper.cs

#
C# | 573 lines | 385 code | 100 blank | 88 comment | 100 complexity | 3d0cfae67fff74b0ab7cdd375074fe81 MD5 | raw file
  1. namespace MoreTerra
  2. {
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.IO;
  8. using System.Windows.Forms;
  9. using MoreTerra.Utilities;
  10. using MoreTerra.Structures;
  11. using System.ComponentModel;
  12. using MoreTerra.Structures.TerraInfo;
  13. public class WorldMapper
  14. {
  15. private List<Chest> chests;
  16. private Dictionary<MarkerType, List<MarkerLoc>> tileMarkersToAdd;
  17. private Int16[,] tiles;
  18. private World world;
  19. int maxX, maxY;
  20. public int progress = 0;
  21. public WorldMapper()
  22. {
  23. chests = new List<Chest>();
  24. }
  25. public void Initialize()
  26. {
  27. TileData td;
  28. // Now we set DrawMarker for each of the markers we can potentially draw.
  29. // This makes for a much faster lookup to see if we need to draw an marker
  30. // rather than mass calling the DrawMarker function.
  31. for (Int32 i = 0; i < TileProperties.tileTypeDefs.Length; i++)
  32. {
  33. td = TileProperties.tileTypeDefs[i];
  34. if (td.MarkerType != MarkerType.Unknown)
  35. {
  36. td.DrawMarker = SettingsManager.Instance.DrawMarker((Int16) i);
  37. }
  38. }
  39. }
  40. public void OpenWorld()
  41. {
  42. world = new World();
  43. }
  44. public void ProcessWorld(String worldPath, BackgroundWorker bw)
  45. {
  46. tiles = world.ReadAndProcessWorld(worldPath, bw);
  47. if (tiles == null)
  48. return;
  49. progress = 45;
  50. maxX = world.Header.MaxTiles.X;
  51. maxY = world.Header.MaxTiles.Y;
  52. // Reset Marker List
  53. tileMarkersToAdd = new Dictionary<MarkerType, List<MarkerLoc>>();
  54. // I got horribly tired of having to constantly to use ContainsKey searches just to be
  55. // positive we don't try to access a list that is uninitialized so I just started
  56. // them all up here. The drawing code will quickly skip empty ones without drawing
  57. // anyways.
  58. Array m = Enum.GetValues(typeof(MarkerType));
  59. foreach (MarkerType mt in m)
  60. {
  61. tileMarkersToAdd.Add(mt, new List<MarkerLoc>());
  62. }
  63. if (bw != null)
  64. bw.ReportProgress(45, "Processing Chests");
  65. List<String> itemFilters = SettingsManager.Instance.FilterItemStates;
  66. // Read the Chests
  67. this.chests = world.Chests;
  68. foreach (Chest chest in this.chests)
  69. {
  70. progress = (int)(((float)chest.ChestId / (float)Global.ChestMaxNumber) * 5f + 45f);
  71. // See if we are bothering to draw chests at all.
  72. if (SettingsManager.Instance.DrawMarker(chest.Type) == true)
  73. {
  74. // Find out if the chest is relevant to our interests based on what is in it.
  75. foreach (Item item in chest.Items)
  76. {
  77. // If we're not filtering or if we want it
  78. if (!SettingsManager.Instance.FilterChests || itemFilters.Contains(item.Name))
  79. {
  80. // It passed all checks, add it to the list.
  81. if (chest.Type == ChestType.Chest)
  82. tileMarkersToAdd[MarkerType.Chest].Add(new MarkerLoc(chest.Coordinates, 1));
  83. else
  84. tileMarkersToAdd[MarkerType.Chest + (Int32) chest.Type].Add(
  85. new MarkerLoc(chest.Coordinates, 1));
  86. break;
  87. }
  88. }
  89. }
  90. }
  91. if (bw != null)
  92. bw.ReportProgress(50, "Processing Signs");
  93. // Pull all of the Signs out of the file.
  94. foreach (Sign newSign in world.Signs)
  95. {
  96. if (newSign.Active)
  97. {
  98. if (SettingsManager.Instance.DrawMarker(MarkerType.Sign))
  99. {
  100. tileMarkersToAdd[MarkerType.Sign].Add(new MarkerLoc(newSign.Position, 1));
  101. }
  102. }
  103. }
  104. if (bw != null)
  105. bw.ReportProgress(50, "Processing Npcs");
  106. foreach (NPC newNPC in world.Npcs)
  107. {
  108. if (newNPC == null)
  109. continue;
  110. if (newNPC.Type == NPCType.Unknown)
  111. continue;
  112. MarkerType tt = MarkerType.ArmsDealer + (Int32)newNPC.Type;
  113. if (SettingsManager.Instance.DrawMarker(newNPC.Type) == true)
  114. {
  115. // I didn't think we ever needed more than one of each NPC. Then I remembered that
  116. // if conditions are right two nurses and three merchants can spawn.
  117. tileMarkersToAdd[tt].Add(new MarkerLoc(
  118. new Point((Int32) (newNPC.Position.X/16), (Int32) (newNPC.Position.Y/16)), 1));
  119. }
  120. }
  121. progress = 50;
  122. }
  123. // Called during worker_GenerateMap. This will only get used if
  124. // we are only doing a LoadInformation button press call.
  125. public void ReadChests(String worldPath, BackgroundWorker bw)
  126. {
  127. progress = 0;
  128. chests = world.GetChests(worldPath, bw);
  129. }
  130. public Bitmap CreatePreviewPNG(string outputPngPath, BackgroundWorker bw)
  131. {
  132. Boolean useOfficialColors = SettingsManager.Instance.OfficialColors;
  133. Int32 CropAmount;
  134. int row, col;
  135. Bitmap bitmap;
  136. switch(SettingsManager.Instance.CropImageUsing)
  137. {
  138. case 1:
  139. CropAmount = Global.OldLightingCrop;
  140. break;
  141. case 2:
  142. CropAmount = Global.NewLightingCrop;
  143. break;
  144. default:
  145. CropAmount = 0;
  146. break;
  147. }
  148. if (CropAmount > 0)
  149. {
  150. bitmap = new Bitmap(maxX - (2 * CropAmount) - 1, maxY - (2 * CropAmount) - 1
  151. , PixelFormat.Format24bppRgb);
  152. }
  153. else
  154. {
  155. bitmap = new Bitmap(maxX, maxY, PixelFormat.Format24bppRgb);
  156. }
  157. Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
  158. Graphics graphicsHandle = Graphics.FromImage((Image)bitmap);
  159. //graphicsHandle.FillRectangle(new SolidBrush(Constants.Colors.SKY), 0, 0, bitmap.Width, bitmap.Height);
  160. System.Drawing.Imaging.BitmapData bmpData = bitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
  161. IntPtr ptr = bmpData.Scan0;
  162. int bytes = Math.Abs(bmpData.Stride) * bitmap.Height;
  163. byte[] rgbValues = new byte[bytes];
  164. const int byteOffset = 3;
  165. TileData tileInfo;
  166. MarkerType markerType;
  167. Int16 tileType;
  168. Color color;
  169. if (bw != null)
  170. bw.ReportProgress(50, "Drawing Map");
  171. if (CropAmount > 0)
  172. {
  173. for (row = 0; row < CropAmount; row++)
  174. {
  175. for (col = 0; col < maxX; col++)
  176. {
  177. tiles[col, row] = TileProperties.Cropped;
  178. }
  179. }
  180. for (row = maxY - CropAmount - 1; row < maxY; row++)
  181. {
  182. for (col = 0; col < maxX; col++)
  183. {
  184. tiles[col, row] = TileProperties.Cropped;
  185. }
  186. }
  187. for (row = 0; row < maxY; row++)
  188. {
  189. for (col = 0; col < CropAmount; col++)
  190. {
  191. tiles[col, row] = TileProperties.Cropped;
  192. }
  193. }
  194. for (row = 0; row < maxY; row++)
  195. {
  196. for (col = maxX - CropAmount - 1; col < maxX; col++)
  197. {
  198. tiles[col, row] = TileProperties.Cropped;
  199. }
  200. }
  201. }
  202. for (row = 0; row < maxY; row++)
  203. {
  204. progress = (int)(((float)row / (float)maxY) * 40f + 50f);
  205. int index = (bmpData.Stride * (row - CropAmount)) - (1 * byteOffset); //first increment will be 0;
  206. for (col = 0; col < maxX; col++)
  207. {
  208. if (tiles[col, row] == TileProperties.Cropped)
  209. continue;
  210. index += byteOffset; //increase here to avoid adding increments to each continue
  211. tileType = tiles[col, row];
  212. // Skip Walls
  213. if (!SettingsManager.Instance.DrawWalls && tileType > TileProperties.BackgroundOffset)
  214. tileType = TileProperties.BackgroundOffset;
  215. if (tileType < TileProperties.Processed)
  216. {
  217. tileInfo = TileProperties.tileTypeDefs[tileType];
  218. if (tileInfo.DrawMarker)
  219. {
  220. // If we have already processed this then skip it.
  221. Int32 tileCount = 1;
  222. Int32 foundCol = col;
  223. Int32 foundRow = row;
  224. #region AdvancedMarkerComment
  225. /*
  226. * First off, we know from the way the level is processed we never have to
  227. * check up as if something higher than the start existed we'd have hit it
  228. * while processing down anyways.
  229. *
  230. * We could parse around and find only stuff that is directly connected to
  231. * each other and have a "perfect" look but that would take more processing
  232. * than I'm willing to do. Instead we'll keep a bounding box around what
  233. * we've found and use that in the end.
  234. *
  235. * To keep things from getting out of hand from player made clumps of things
  236. * there is a maximum size the box can be.
  237. *
  238. * ----------
  239. * Pseudocode
  240. * ----------
  241. * First we check to see if we expanded since last run.
  242. * Then we check all rows but the bottom row for further expansion.
  243. * One pass one we'll skip the top row as it's also the bottom row.
  244. *
  245. * Next we scan the side edges of the bottom row to try and expand.
  246. * Then we scan the row below the bottom for vertical expansion.
  247. *
  248. * If there was vertical expansion we now have a new bottom row and
  249. * so we loop through scanning below the bottom row again.
  250. *
  251. * Once we run out of vertical expansion we'll loop back (if we expanded
  252. * at all) and rescan the edges to see if by expanding we opened up
  253. * something new in those newly opened sides.
  254. *
  255. * Once we do one full pass without any new expansion we have our final
  256. * bounding box so we scan straight through from one end to the other
  257. * and call everything that matches that we find in it our marker block.
  258. */
  259. #endregion
  260. Int32 boundsMax = 32;
  261. Rectangle bounds = new Rectangle(col, row, 1, 1);
  262. Int32 screenMaxWidth = world.Header.MaxTiles.X;
  263. Int32 screenMaxHeight = world.Header.MaxTiles.Y;
  264. Boolean expandedHoriz = false;
  265. Boolean expandedVert = false;
  266. Boolean doneProcessing = true;
  267. Int32 i, j;
  268. do
  269. {
  270. doneProcessing = true;
  271. for (j = bounds.Y; j < row + boundsMax; j++)
  272. {
  273. if (bounds.Bottom >= screenMaxHeight || bounds.Height >= boundsMax)
  274. break;
  275. expandedVert = false;
  276. // We only scan the sides if we either changed the width since last
  277. // loop or we are on the bottom row.
  278. if (expandedHoriz == true || j == (bounds.Bottom - 1))
  279. {
  280. expandedHoriz = false;
  281. #region ExpandLeft
  282. if (tiles[bounds.X, j] == tileType)
  283. {
  284. for (i = bounds.X - 1; i > (col - boundsMax); i--)
  285. {
  286. if (i <= 0)
  287. break;
  288. if (tiles[i, j] == tileType)
  289. {
  290. bounds.X = i;
  291. bounds.Width++;
  292. expandedHoriz = true;
  293. doneProcessing = false;
  294. }
  295. else
  296. {
  297. break;
  298. }
  299. }
  300. }
  301. #endregion
  302. #region ExpandRight
  303. if (tiles[bounds.Right - 1, j] == tileType)
  304. {
  305. for (i = bounds.Right; i < (bounds.X + boundsMax); i++)
  306. {
  307. if (i >= screenMaxWidth)
  308. break;
  309. if (tiles[i, j] == tileType)
  310. {
  311. bounds.Width++;
  312. expandedHoriz = true;
  313. doneProcessing = false;
  314. if (bounds.Width == boundsMax)
  315. break;
  316. }
  317. else
  318. {
  319. break;
  320. }
  321. }
  322. }
  323. #endregion
  324. }
  325. if (j == (bounds.Bottom - 1))
  326. {
  327. #region ExpandDown
  328. if (j + 1 < screenMaxHeight && bounds.Height < boundsMax)
  329. {
  330. for (i = bounds.X; i < (bounds.Right); i++)
  331. {
  332. if (tiles[i, j] == tileType)
  333. {
  334. if (tiles[i, j + 1] == tileType)
  335. {
  336. if (j + 1 >= bounds.Bottom)
  337. {
  338. bounds.Height++;
  339. expandedVert = true;
  340. doneProcessing = false;
  341. break;
  342. }
  343. }
  344. }
  345. }
  346. }
  347. #endregion
  348. }
  349. if (expandedVert == false)
  350. break;
  351. }
  352. } while (doneProcessing == false);
  353. Int32 tilePos;
  354. tileCount = 0;
  355. if (useOfficialColors == true)
  356. color = tileInfo.OfficialColor;
  357. else
  358. color = tileInfo.Color;
  359. for (j = bounds.Y; j < bounds.Bottom; j++)
  360. for (i = bounds.X; i < bounds.Right; i++)
  361. if (tiles[i, j] == tileType)
  362. {
  363. tileCount++;
  364. tiles[i, j] = TileProperties.Processed;
  365. tilePos = (j - CropAmount) * bmpData.Stride +
  366. (i - CropAmount) * byteOffset;
  367. rgbValues[tilePos] = color.B;
  368. rgbValues[tilePos + 1] = color.G;
  369. rgbValues[tilePos + 2] = color.R;
  370. }
  371. foundCol = (int)(bounds.X + (bounds.Width / 2));
  372. foundRow = (int)(bounds.Y + (bounds.Height / 2));
  373. markerType = TileProperties.tileTypeDefs[tileType].MarkerType;
  374. tileMarkersToAdd[markerType].Add(new MarkerLoc(new Point(foundCol, foundRow), tileCount));
  375. }
  376. }
  377. // This used to not draw at all if you chose to put a marker down for that type, which makes
  378. // sense as the marker did cover it. However this also caused a bug where things you had not
  379. // chosen to put a marker didn't draw their pixel color in. There was a different fix for
  380. // this but combining the fact that skipping that draw saved very little time and that
  381. // with the new "One marker per set of items" instead of "One marker per item" mechanic
  382. // means that you might see some of the color hanging out the edge.
  383. if (tileType != TileProperties.Processed)
  384. {
  385. if (useOfficialColors)
  386. color = TileProperties.tileTypeDefs[tileType].OfficialColor;
  387. else
  388. color = TileProperties.tileTypeDefs[tileType].Color;
  389. rgbValues[index] = color.B;
  390. rgbValues[index + 1] = color.G;
  391. rgbValues[index + 2] = color.R;
  392. }
  393. }
  394. }
  395. System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
  396. bitmap.UnlockBits(bmpData);
  397. // Add Spawn
  398. if (SettingsManager.Instance.DrawMarker(MarkerType.Spawn) == true)
  399. {
  400. tileMarkersToAdd[MarkerType.Spawn].Add(new MarkerLoc(new Point(
  401. world.Header.SpawnPoint.X, world.Header.SpawnPoint.Y), 1));
  402. }
  403. if (bw != null)
  404. bw.ReportProgress(90, "Drawing markers");
  405. Int32 count = 0;
  406. // Draw Markers
  407. foreach (KeyValuePair<MarkerType, List<MarkerLoc>> kv in tileMarkersToAdd)
  408. {
  409. if (kv.Key == MarkerType.Unknown)
  410. continue;
  411. progress = (Int32)(((Double)count / tileMarkersToAdd.Count) * 10f + 90f);
  412. Bitmap markerBitmap = ResourceManager.Instance.GetMarker(kv.Key);
  413. foreach (MarkerLoc sl in kv.Value)
  414. {
  415. int x = Math.Max((int)sl.pv.X - (markerBitmap.Width / 2) - CropAmount, 0);
  416. int y = Math.Max((int)sl.pv.Y - (markerBitmap.Height / 2) - CropAmount, 0);
  417. if (x > maxX || y > maxY) continue;
  418. graphicsHandle.DrawImage(markerBitmap, x, y);
  419. }
  420. }
  421. if (bw != null)
  422. bw.ReportProgress(99, "Saving image");
  423. bitmap.Save(outputPngPath, ImageFormat.Png);
  424. progress = 100;
  425. return bitmap;
  426. }
  427. public void Cleanup()
  428. {
  429. // We will set everything to null, except for Chests as we still use them
  430. // to do our Chest list sorting.
  431. this.tileMarkersToAdd = null;
  432. this.tiles = null;
  433. this.world = null;
  434. }
  435. // This code is just to runs tests with.
  436. /* public void ScanAndCompare(String worldPath, BackgroundWorker bw)
  437. {
  438. World.TileImportance[] imp;
  439. world = new World();
  440. imp = world.ScanWorld(worldPath, bw);
  441. for (Int32 j = 0; j < imp.GetLength(0); j++)
  442. {
  443. for (Int32 i = 0; i < 256; i++)
  444. {
  445. if (imp[j].isKnown((Byte)i))
  446. {
  447. if (imp[j].isImportant((Byte)i) != tileTypeDefs[i].IsImportant)
  448. i = i;
  449. }
  450. }
  451. }
  452. }*/
  453. #region GetSet Functions
  454. public List<Chest> Chests
  455. {
  456. get
  457. {
  458. return this.chests;
  459. }
  460. }
  461. public World World
  462. {
  463. get
  464. {
  465. return world;
  466. }
  467. }
  468. #endregion
  469. }
  470. }