ResourcesTool.cs (12357B)
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.Controls; 16 using MobiusEditor.Event; 17 using MobiusEditor.Interface; 18 using MobiusEditor.Model; 19 using MobiusEditor.Utility; 20 using MobiusEditor.Widgets; 21 using System; 22 using System.Collections.Generic; 23 using System.Drawing; 24 using System.Linq; 25 using System.Windows.Forms; 26 27 namespace MobiusEditor.Tools 28 { 29 public class ResourcesTool : ViewTool 30 { 31 private readonly Label totalResourcesLbl; 32 private readonly NumericUpDown brushSizeNud; 33 private readonly CheckBox gemsCheckBox; 34 35 private bool placementMode; 36 private bool additivePlacement; 37 38 private readonly Dictionary<int, Overlay> undoOverlays = new Dictionary<int, Overlay>(); 39 private readonly Dictionary<int, Overlay> redoOverlays = new Dictionary<int, Overlay>(); 40 41 public ResourcesTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, Label totalResourcesLbl, NumericUpDown brushSizeNud, CheckBox gemsCheckBox, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url) 42 : base(mapPanel, layers, statusLbl, plugin, url) 43 { 44 this.mapPanel.MouseDown += MapPanel_MouseDown; 45 this.mapPanel.MouseUp += MapPanel_MouseUp; 46 (this.mapPanel as Control).KeyDown += ResourceTool_KeyDown; 47 48 this.totalResourcesLbl = totalResourcesLbl; 49 this.brushSizeNud = brushSizeNud; 50 this.gemsCheckBox = gemsCheckBox; 51 52 this.brushSizeNud.ValueChanged += BrushSizeNud_ValueChanged; 53 54 navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged; 55 navigationWidget.MouseoverSize = new Size((int)brushSizeNud.Value, (int)brushSizeNud.Value); 56 57 url.Undone += Url_UndoRedo; 58 url.Redone += Url_UndoRedo; 59 60 Update(); 61 62 UpdateStatus(); 63 } 64 65 private void Url_UndoRedo(object sender, EventArgs e) 66 { 67 Update(); 68 } 69 70 private void BrushSizeNud_ValueChanged(object sender, EventArgs e) 71 { 72 navigationWidget.MouseoverSize = new Size((int)brushSizeNud.Value, (int)brushSizeNud.Value); 73 } 74 75 private void ResourceTool_KeyDown(object sender, KeyEventArgs e) 76 { 77 if (e.KeyCode == Keys.OemOpenBrackets) 78 { 79 brushSizeNud.DownButton(); 80 mapPanel.Invalidate(); 81 } 82 else if (e.KeyCode == Keys.OemCloseBrackets) 83 { 84 brushSizeNud.UpButton(); 85 mapPanel.Invalidate(); 86 } 87 } 88 89 private void MapPanel_MouseDown(object sender, MouseEventArgs e) 90 { 91 if (e.Button == MouseButtons.Left) 92 { 93 if (!placementMode) 94 { 95 EnterPlacementMode(true); 96 AddResource(navigationWidget.MouseCell); 97 } 98 } 99 else if (e.Button == MouseButtons.Right) 100 { 101 if (!placementMode) 102 { 103 EnterPlacementMode(false); 104 RemoveResource(navigationWidget.MouseCell); 105 } 106 } 107 } 108 109 private void MapPanel_MouseUp(object sender, MouseEventArgs e) 110 { 111 if (placementMode) 112 { 113 if (((e.Button == MouseButtons.Left) && additivePlacement) || 114 ((e.Button == MouseButtons.Right) && !additivePlacement)) 115 { 116 ExitPlacementMode(); 117 } 118 } 119 120 if ((undoOverlays.Count > 0) || (redoOverlays.Count > 0)) 121 { 122 CommitChange(); 123 } 124 } 125 126 private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e) 127 { 128 if (placementMode) 129 { 130 if (additivePlacement) 131 { 132 AddResource(e.NewCell); 133 } 134 else 135 { 136 RemoveResource(e.NewCell); 137 } 138 } 139 140 if (brushSizeNud.Value > 1) 141 { 142 foreach (var cell in new Point[] { e.OldCell, e.NewCell }) 143 { 144 mapPanel.Invalidate(mapPanel.MapToClient(new Rectangle( 145 new Point(cell.X - ((int)brushSizeNud.Value / 2), cell.Y - ((int)brushSizeNud.Value / 2)), 146 new Size((int)brushSizeNud.Value, (int)brushSizeNud.Value) 147 ))); 148 } 149 } 150 } 151 152 private void AddResource(Point location) 153 { 154 Rectangle rectangle = new Rectangle(location, new Size(1, 1)); 155 rectangle.Inflate(navigationWidget.MouseoverSize.Width / 2, navigationWidget.MouseoverSize.Height / 2); 156 foreach (var subLocation in rectangle.Points()) 157 { 158 if ((subLocation.Y == 0) || (subLocation.Y == (map.Metrics.Height - 1))) 159 { 160 continue; 161 } 162 163 if (map.Metrics.GetCell(subLocation, out int cell)) 164 { 165 if (map.Overlay[cell] == null) 166 { 167 var resourceType = gemsCheckBox.Checked ? 168 map.OverlayTypes.Where(t => t.IsGem).FirstOrDefault() : 169 map.OverlayTypes.Where(t => t.IsTiberiumOrGold).FirstOrDefault(); 170 if (resourceType != null) 171 { 172 if (!undoOverlays.ContainsKey(cell)) 173 { 174 undoOverlays[cell] = map.Overlay[cell]; 175 } 176 177 var overlay = new Overlay { Type = resourceType, Icon = 0 }; 178 map.Overlay[cell] = overlay; 179 redoOverlays[cell] = overlay; 180 181 plugin.Dirty = true; 182 } 183 } 184 } 185 } 186 187 rectangle.Inflate(1, 1); 188 mapPanel.Invalidate(map, rectangle); 189 190 Update(); 191 } 192 193 private void RemoveResource(Point location) 194 { 195 Rectangle rectangle = new Rectangle(location, new Size(1, 1)); 196 rectangle.Inflate(navigationWidget.MouseoverSize.Width / 2, navigationWidget.MouseoverSize.Height / 2); 197 foreach (var subLocation in rectangle.Points()) 198 { 199 if (map.Metrics.GetCell(subLocation, out int cell)) 200 { 201 if (map.Overlay[cell]?.Type.IsResource ?? false) 202 { 203 if (!undoOverlays.ContainsKey(cell)) 204 { 205 undoOverlays[cell] = map.Overlay[cell]; 206 } 207 208 map.Overlay[cell] = null; 209 redoOverlays[cell] = null; 210 211 plugin.Dirty = true; 212 } 213 } 214 } 215 216 rectangle.Inflate(1, 1); 217 mapPanel.Invalidate(map, rectangle); 218 219 Update(); 220 } 221 222 private void EnterPlacementMode(bool additive) 223 { 224 if (placementMode) 225 { 226 return; 227 } 228 229 placementMode = true; 230 additivePlacement = additive; 231 232 UpdateStatus(); 233 } 234 235 private void ExitPlacementMode() 236 { 237 if (!placementMode) 238 { 239 return; 240 } 241 242 placementMode = false; 243 244 UpdateStatus(); 245 } 246 247 private void CommitChange() 248 { 249 var undoOverlays2 = new Dictionary<int, Overlay>(undoOverlays); 250 void undoAction(UndoRedoEventArgs e) 251 { 252 foreach (var kv in undoOverlays2) 253 { 254 e.Map.Overlay[kv.Key] = kv.Value; 255 } 256 e.MapPanel.Invalidate(e.Map, undoOverlays2.Keys.Select(k => 257 { 258 e.Map.Metrics.GetLocation(k, out Point location); 259 var rectangle = new Rectangle(location, new Size(1, 1)); 260 rectangle.Inflate(1, 1); 261 return rectangle; 262 })); 263 } 264 265 var redoOverlays2 = new Dictionary<int, Overlay>(redoOverlays); 266 void redoAction(UndoRedoEventArgs e) 267 { 268 foreach (var kv in redoOverlays2) 269 { 270 e.Map.Overlay[kv.Key] = kv.Value; 271 } 272 e.MapPanel.Invalidate(e.Map, redoOverlays2.Keys.Select(k => 273 { 274 e.Map.Metrics.GetLocation(k, out Point location); 275 var rectangle = new Rectangle(location, new Size(1, 1)); 276 rectangle.Inflate(1, 1); 277 return rectangle; 278 })); 279 } 280 281 undoOverlays.Clear(); 282 redoOverlays.Clear(); 283 284 url.Track(undoAction, redoAction); 285 } 286 287 private void Update() 288 { 289 totalResourcesLbl.Text = map.TotalResources.ToString(); 290 291 if (map.OverlayTypes.Any(t => t.IsGem)) 292 { 293 gemsCheckBox.Visible = true; 294 } 295 else 296 { 297 gemsCheckBox.Visible = false; 298 gemsCheckBox.Checked = false; 299 } 300 } 301 302 private void UpdateStatus() 303 { 304 if (placementMode) 305 { 306 if (additivePlacement) 307 { 308 statusLbl.Text = "Drag mouse to add resources"; 309 } 310 else 311 { 312 statusLbl.Text = "Drag mouse to remove resources"; 313 } 314 } 315 else 316 { 317 statusLbl.Text = "Left-Click drag to add resources, Right-Click drag to remove resources"; 318 } 319 } 320 321 protected override void PostRenderMap(Graphics graphics) 322 { 323 base.PostRenderMap(graphics); 324 325 var resourcePen = new Pen(Color.Green, 4.0f); 326 foreach (var (cell, overlay) in map.Overlay) 327 { 328 if (overlay.Type.IsResource) 329 { 330 map.Metrics.GetLocation(cell, out Point topLeft); 331 var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize); 332 graphics.DrawRectangle(resourcePen, bounds); 333 } 334 } 335 } 336 337 #region IDisposable Support 338 private bool disposedValue = false; 339 340 protected override void Dispose(bool disposing) 341 { 342 if (!disposedValue) 343 { 344 if (disposing) 345 { 346 mapPanel.MouseDown -= MapPanel_MouseDown; 347 mapPanel.MouseUp -= MapPanel_MouseUp; 348 (mapPanel as Control).KeyDown -= ResourceTool_KeyDown; 349 350 brushSizeNud.ValueChanged -= BrushSizeNud_ValueChanged; 351 352 navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged; 353 354 url.Undone -= Url_UndoRedo; 355 url.Redone -= Url_UndoRedo; 356 } 357 disposedValue = true; 358 } 359 360 base.Dispose(disposing); 361 } 362 #endregion 363 } 364 }