OccupierSet.cs (9209B)
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 9 // The Command & Conquer Map Editor and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 using MobiusEditor.Interface; 16 using System; 17 using System.Collections; 18 using System.Collections.Generic; 19 using System.Drawing; 20 using System.Linq; 21 22 namespace MobiusEditor.Model 23 { 24 public class OccupierAddedEventArgs<T> : EventArgs 25 { 26 public readonly int Cell; 27 28 public readonly Point Location; 29 30 public readonly T Occupier; 31 32 public OccupierAddedEventArgs(CellMetrics metrics, int cell, T occupier) 33 { 34 Cell = cell; 35 metrics.GetLocation(cell, out Location); 36 Occupier = occupier; 37 } 38 39 public OccupierAddedEventArgs(CellMetrics metrics, Point location, T occupier) 40 { 41 Location = location; 42 metrics.GetCell(location, out Cell); 43 Occupier = occupier; 44 } 45 } 46 47 public class OccupierRemovedEventArgs<T> : EventArgs 48 { 49 public readonly int Cell; 50 51 public readonly Point Location; 52 53 public readonly T Occupier; 54 55 public OccupierRemovedEventArgs(CellMetrics metrics, int cell, T occupier) 56 { 57 Cell = cell; 58 metrics.GetLocation(cell, out Location); 59 Occupier = occupier; 60 } 61 62 public OccupierRemovedEventArgs(CellMetrics metrics, Point location, T occupier) 63 { 64 Location = location; 65 metrics.GetCell(location, out Cell); 66 Occupier = occupier; 67 } 68 } 69 70 public class OccupierSet<T> : IEnumerable<(Point Location, T Occupier)>, IEnumerable where T : class, ICellOccupier 71 { 72 private readonly CellMetrics metrics; 73 private readonly Dictionary<T, Point> occupiers = new Dictionary<T, Point>(); 74 private readonly T[,] occupierCells; 75 76 public T this[Point location] => this[location.X, location.Y]; 77 78 public T this[int x, int y] => Contains(x, y) ? occupierCells[y, x] : null; 79 80 public T this[int cell] => metrics.GetLocation(cell, out Point location) ? this[location] : null; 81 82 public Point? this[T occupier] => occupiers.ContainsKey(occupier) ? occupiers[occupier] : default; 83 84 public IEnumerable<T> Occupiers => occupiers.Keys; 85 86 public event EventHandler<OccupierAddedEventArgs<T>> OccupierAdded; 87 public event EventHandler<OccupierRemovedEventArgs<T>> OccupierRemoved; 88 public event EventHandler<EventArgs> Cleared; 89 90 public OccupierSet(CellMetrics metrics) 91 { 92 this.metrics = metrics; 93 occupierCells = new T[metrics.Height, metrics.Width]; 94 } 95 96 public bool CanAdd(Point location, T occupier, bool[,] occupyMask) 97 { 98 if ((occupier == null) || Contains(occupier)) 99 { 100 return false; 101 } 102 103 var occupyPoints = GetOccupyPoints(location, occupyMask).ToArray(); 104 return !occupyPoints.Any(p => !Contains(p) || (this[p] != null)); 105 } 106 107 public bool CanAdd(int x, int y, T occupier, bool[,] occupyMask) => CanAdd(new Point(x, y), occupier, occupyMask); 108 109 public bool CanAdd(int cell, T occupier, bool[,] occupyMask) => metrics.GetLocation(cell, out Point location) ? CanAdd(location, occupier, occupyMask) : false; 110 111 public bool CanAdd(Point location, T occupier) => (occupier != null) ? CanAdd(location, occupier, occupier.OccupyMask) : false; 112 113 public bool CanAdd(int x, int y, T occupier) => (occupier != null) ? CanAdd(x, y, occupier, occupier.OccupyMask) : false; 114 115 public bool CanAdd(int cell, T occupier) => (occupier != null) ? CanAdd(cell, occupier, occupier.OccupyMask) : false; 116 117 public bool Add(Point location, T occupier, bool[,] occupyMask) 118 { 119 if (!DoAdd(location, occupier, occupyMask)) 120 { 121 return false; 122 } 123 124 OnOccupierAdded(new OccupierAddedEventArgs<T>(metrics, location, occupier)); 125 return true; 126 } 127 128 public bool Add(int x, int y, T occupier, bool[,] occupyMask) => Add(new Point(x, y), occupier, occupyMask); 129 130 public bool Add(int cell, T occupier, bool[,] occupyMask) => metrics.GetLocation(cell, out Point location) ? Add(location, occupier, occupyMask) : false; 131 132 public bool Add(Point location, T occupier) => (occupier != null) ? Add(location, occupier, occupier.OccupyMask) : false; 133 134 public bool Add(int x, int y, T occupier) => (occupier != null) ? Add(x, y, occupier, occupier.OccupyMask) : false; 135 136 public bool Add(int cell, T occupier) => (occupier != null) ? Add(cell, occupier, occupier.OccupyMask) : false; 137 138 public void Clear() 139 { 140 occupiers.Clear(); 141 Array.Clear(occupierCells, 0, occupierCells.Length); 142 OnCleared(); 143 } 144 145 public bool Contains(int x, int y) => ((x >= 0) && (x < occupierCells.GetLength(1)) && (y >= 0) && (y < occupierCells.GetLength(0))); 146 147 public bool Contains(Point location) => Contains(location.X, location.Y); 148 149 public bool Contains(int cell) => metrics.GetLocation(cell, out Point location) ? Contains(location) : false; 150 151 public bool Contains(T occupier) => occupiers.ContainsKey(occupier); 152 153 public IEnumerator<(Point Location, T Occupier)> GetEnumerator() => occupiers.Select(kv => (kv.Value, kv.Key)).GetEnumerator(); 154 155 public bool Remove(T occupier) 156 { 157 var oldLocation = this[occupier]; 158 if (!DoRemove(occupier)) 159 { 160 return false; 161 } 162 163 OnOccupierRemoved(new OccupierRemovedEventArgs<T>(metrics, oldLocation.Value, occupier)); 164 return true; 165 } 166 167 public bool Remove(Point location) => Remove(this[location]); 168 169 public bool Remove(int x, int y) => Remove(new Point(x, y)); 170 171 public bool Remove(int cell) => metrics.GetLocation(cell, out Point location) ? Remove(location) : false; 172 173 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)); 174 175 protected virtual void OnOccupierAdded(OccupierAddedEventArgs<T> e) 176 { 177 OccupierAdded?.Invoke(this, e); 178 } 179 180 protected virtual void OnOccupierRemoved(OccupierRemovedEventArgs<T> e) 181 { 182 OccupierRemoved?.Invoke(this, e); 183 } 184 185 protected virtual void OnCleared() 186 { 187 Cleared?.Invoke(this, new EventArgs()); 188 } 189 190 IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 191 192 private bool DoAdd(Point location, T occupier, bool[,] occupyMask) 193 { 194 if ((occupier == null) || Contains(occupier)) 195 { 196 return false; 197 } 198 199 var occupyPoints = GetOccupyPoints(location, occupyMask).ToArray(); 200 if (occupyPoints.Any(p => !Contains(p) || (this[p] != null))) 201 { 202 return false; 203 } 204 205 occupiers[occupier] = location; 206 foreach (var p in occupyPoints) 207 { 208 occupierCells[p.Y, p.X] = occupier; 209 } 210 return true; 211 } 212 213 private bool DoRemove(T occupier) 214 { 215 if ((occupier == null) || !occupiers.TryGetValue(occupier, out Point location)) 216 { 217 return false; 218 } 219 220 occupiers.Remove(occupier); 221 for (var y = location.Y; y < metrics.Height; ++y) 222 { 223 for (var x = location.X; x < metrics.Width; ++x) 224 { 225 if (occupierCells[y, x] == occupier) 226 { 227 occupierCells[y, x] = null; 228 } 229 } 230 } 231 return true; 232 } 233 234 private static IEnumerable<Point> GetOccupyPoints(Point location, bool[,] occupyMask) 235 { 236 for (var y = 0; y < occupyMask.GetLength(0); ++y) 237 { 238 for (var x = 0; x < occupyMask.GetLength(1); ++x) 239 { 240 if (occupyMask[y, x]) 241 { 242 yield return location + new Size(x, y); 243 } 244 } 245 } 246 } 247 248 private static IEnumerable<Point> GetOccupyPoints(Point location, T occupier) => GetOccupyPoints(location, occupier.OccupyMask); 249 } 250 }