OccupierSet.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // The Command & Conquer Map Editor and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // The Command & Conquer Map Editor and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. using MobiusEditor.Interface;
  15. using System;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Drawing;
  19. using System.Linq;
  20. namespace MobiusEditor.Model
  21. {
  22. public class OccupierAddedEventArgs<T> : EventArgs
  23. {
  24. public readonly int Cell;
  25. public readonly Point Location;
  26. public readonly T Occupier;
  27. public OccupierAddedEventArgs(CellMetrics metrics, int cell, T occupier)
  28. {
  29. Cell = cell;
  30. metrics.GetLocation(cell, out Location);
  31. Occupier = occupier;
  32. }
  33. public OccupierAddedEventArgs(CellMetrics metrics, Point location, T occupier)
  34. {
  35. Location = location;
  36. metrics.GetCell(location, out Cell);
  37. Occupier = occupier;
  38. }
  39. }
  40. public class OccupierRemovedEventArgs<T> : EventArgs
  41. {
  42. public readonly int Cell;
  43. public readonly Point Location;
  44. public readonly T Occupier;
  45. public OccupierRemovedEventArgs(CellMetrics metrics, int cell, T occupier)
  46. {
  47. Cell = cell;
  48. metrics.GetLocation(cell, out Location);
  49. Occupier = occupier;
  50. }
  51. public OccupierRemovedEventArgs(CellMetrics metrics, Point location, T occupier)
  52. {
  53. Location = location;
  54. metrics.GetCell(location, out Cell);
  55. Occupier = occupier;
  56. }
  57. }
  58. public class OccupierSet<T> : IEnumerable<(Point Location, T Occupier)>, IEnumerable where T : class, ICellOccupier
  59. {
  60. private readonly CellMetrics metrics;
  61. private readonly Dictionary<T, Point> occupiers = new Dictionary<T, Point>();
  62. private readonly T[,] occupierCells;
  63. public T this[Point location] => this[location.X, location.Y];
  64. public T this[int x, int y] => Contains(x, y) ? occupierCells[y, x] : null;
  65. public T this[int cell] => metrics.GetLocation(cell, out Point location) ? this[location] : null;
  66. public Point? this[T occupier] => occupiers.ContainsKey(occupier) ? occupiers[occupier] : default;
  67. public IEnumerable<T> Occupiers => occupiers.Keys;
  68. public event EventHandler<OccupierAddedEventArgs<T>> OccupierAdded;
  69. public event EventHandler<OccupierRemovedEventArgs<T>> OccupierRemoved;
  70. public event EventHandler<EventArgs> Cleared;
  71. public OccupierSet(CellMetrics metrics)
  72. {
  73. this.metrics = metrics;
  74. occupierCells = new T[metrics.Height, metrics.Width];
  75. }
  76. public bool CanAdd(Point location, T occupier, bool[,] occupyMask)
  77. {
  78. if ((occupier == null) || Contains(occupier))
  79. {
  80. return false;
  81. }
  82. var occupyPoints = GetOccupyPoints(location, occupyMask).ToArray();
  83. return !occupyPoints.Any(p => !Contains(p) || (this[p] != null));
  84. }
  85. public bool CanAdd(int x, int y, T occupier, bool[,] occupyMask) => CanAdd(new Point(x, y), occupier, occupyMask);
  86. public bool CanAdd(int cell, T occupier, bool[,] occupyMask) => metrics.GetLocation(cell, out Point location) ? CanAdd(location, occupier, occupyMask) : false;
  87. public bool CanAdd(Point location, T occupier) => (occupier != null) ? CanAdd(location, occupier, occupier.OccupyMask) : false;
  88. public bool CanAdd(int x, int y, T occupier) => (occupier != null) ? CanAdd(x, y, occupier, occupier.OccupyMask) : false;
  89. public bool CanAdd(int cell, T occupier) => (occupier != null) ? CanAdd(cell, occupier, occupier.OccupyMask) : false;
  90. public bool Add(Point location, T occupier, bool[,] occupyMask)
  91. {
  92. if (!DoAdd(location, occupier, occupyMask))
  93. {
  94. return false;
  95. }
  96. OnOccupierAdded(new OccupierAddedEventArgs<T>(metrics, location, occupier));
  97. return true;
  98. }
  99. public bool Add(int x, int y, T occupier, bool[,] occupyMask) => Add(new Point(x, y), occupier, occupyMask);
  100. public bool Add(int cell, T occupier, bool[,] occupyMask) => metrics.GetLocation(cell, out Point location) ? Add(location, occupier, occupyMask) : false;
  101. public bool Add(Point location, T occupier) => (occupier != null) ? Add(location, occupier, occupier.OccupyMask) : false;
  102. public bool Add(int x, int y, T occupier) => (occupier != null) ? Add(x, y, occupier, occupier.OccupyMask) : false;
  103. public bool Add(int cell, T occupier) => (occupier != null) ? Add(cell, occupier, occupier.OccupyMask) : false;
  104. public void Clear()
  105. {
  106. occupiers.Clear();
  107. Array.Clear(occupierCells, 0, occupierCells.Length);
  108. OnCleared();
  109. }
  110. public bool Contains(int x, int y) => ((x >= 0) && (x < occupierCells.GetLength(1)) && (y >= 0) && (y < occupierCells.GetLength(0)));
  111. public bool Contains(Point location) => Contains(location.X, location.Y);
  112. public bool Contains(int cell) => metrics.GetLocation(cell, out Point location) ? Contains(location) : false;
  113. public bool Contains(T occupier) => occupiers.ContainsKey(occupier);
  114. public IEnumerator<(Point Location, T Occupier)> GetEnumerator() => occupiers.Select(kv => (kv.Value, kv.Key)).GetEnumerator();
  115. public bool Remove(T occupier)
  116. {
  117. var oldLocation = this[occupier];
  118. if (!DoRemove(occupier))
  119. {
  120. return false;
  121. }
  122. OnOccupierRemoved(new OccupierRemovedEventArgs<T>(metrics, oldLocation.Value, occupier));
  123. return true;
  124. }
  125. public bool Remove(Point location) => Remove(this[location]);
  126. public bool Remove(int x, int y) => Remove(new Point(x, y));
  127. public bool Remove(int cell) => metrics.GetLocation(cell, out Point location) ? Remove(location) : false;
  128. public IEnumerable<(Point Location, U Occupier)> OfType<U>() where U : T => this.Where(i => i.Occupier is U).Select(i => (i.Location, (U)i.Occupier));
  129. protected virtual void OnOccupierAdded(OccupierAddedEventArgs<T> e)
  130. {
  131. OccupierAdded?.Invoke(this, e);
  132. }
  133. protected virtual void OnOccupierRemoved(OccupierRemovedEventArgs<T> e)
  134. {
  135. OccupierRemoved?.Invoke(this, e);
  136. }
  137. protected virtual void OnCleared()
  138. {
  139. Cleared?.Invoke(this, new EventArgs());
  140. }
  141. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  142. private bool DoAdd(Point location, T occupier, bool[,] occupyMask)
  143. {
  144. if ((occupier == null) || Contains(occupier))
  145. {
  146. return false;
  147. }
  148. var occupyPoints = GetOccupyPoints(location, occupyMask).ToArray();
  149. if (occupyPoints.Any(p => !Contains(p) || (this[p] != null)))
  150. {
  151. return false;
  152. }
  153. occupiers[occupier] = location;
  154. foreach (var p in occupyPoints)
  155. {
  156. occupierCells[p.Y, p.X] = occupier;
  157. }
  158. return true;
  159. }
  160. private bool DoRemove(T occupier)
  161. {
  162. if ((occupier == null) || !occupiers.TryGetValue(occupier, out Point location))
  163. {
  164. return false;
  165. }
  166. occupiers.Remove(occupier);
  167. for (var y = location.Y; y < metrics.Height; ++y)
  168. {
  169. for (var x = location.X; x < metrics.Width; ++x)
  170. {
  171. if (occupierCells[y, x] == occupier)
  172. {
  173. occupierCells[y, x] = null;
  174. }
  175. }
  176. }
  177. return true;
  178. }
  179. private static IEnumerable<Point> GetOccupyPoints(Point location, bool[,] occupyMask)
  180. {
  181. for (var y = 0; y < occupyMask.GetLength(0); ++y)
  182. {
  183. for (var x = 0; x < occupyMask.GetLength(1); ++x)
  184. {
  185. if (occupyMask[y, x])
  186. {
  187. yield return location + new Size(x, y);
  188. }
  189. }
  190. }
  191. }
  192. private static IEnumerable<Point> GetOccupyPoints(Point location, T occupier) => GetOccupyPoints(location, occupier.OccupyMask);
  193. }
  194. }