OverlaysTool.cs (11223B)
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.Drawing; 23 using System.Linq; 24 using System.Windows.Forms; 25 26 namespace MobiusEditor.Tools 27 { 28 public class OverlaysTool : ViewTool 29 { 30 private readonly TypeComboBox overlayTypeComboBox; 31 private readonly MapPanel overlayTypeMapPanel; 32 33 private Map previewMap; 34 protected override Map RenderMap => previewMap; 35 36 private bool placementMode; 37 38 private OverlayType selectedOverlayType; 39 private OverlayType SelectedOverlayType 40 { 41 get => selectedOverlayType; 42 set 43 { 44 if (selectedOverlayType != value) 45 { 46 if (placementMode && (selectedOverlayType != null)) 47 { 48 mapPanel.Invalidate(map, navigationWidget.MouseCell); 49 } 50 51 selectedOverlayType = value; 52 overlayTypeComboBox.SelectedValue = selectedOverlayType; 53 54 RefreshMapPanel(); 55 } 56 } 57 } 58 59 public OverlaysTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox overlayTypeComboBox, MapPanel overlayTypeMapPanel, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url) 60 : base(mapPanel, layers, statusLbl, plugin, url) 61 { 62 previewMap = map; 63 64 this.mapPanel.MouseDown += MapPanel_MouseDown; 65 this.mapPanel.MouseMove += MapPanel_MouseMove; 66 (this.mapPanel as Control).KeyDown += OverlaysTool_KeyDown; 67 (this.mapPanel as Control).KeyUp += OverlaysTool_KeyUp; 68 69 this.overlayTypeComboBox = overlayTypeComboBox; 70 this.overlayTypeComboBox.SelectedIndexChanged += OverlayTypeComboBox_SelectedIndexChanged; 71 72 this.overlayTypeMapPanel = overlayTypeMapPanel; 73 this.overlayTypeMapPanel.BackColor = Color.White; 74 this.overlayTypeMapPanel.MaxZoom = 1; 75 76 navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged; 77 78 SelectedOverlayType = this.overlayTypeComboBox.Types.First() as OverlayType; 79 80 UpdateStatus(); 81 } 82 83 private void OverlayTypeComboBox_SelectedIndexChanged(object sender, EventArgs e) 84 { 85 SelectedOverlayType = overlayTypeComboBox.SelectedValue as OverlayType; 86 } 87 88 private void OverlaysTool_KeyDown(object sender, KeyEventArgs e) 89 { 90 if (e.KeyCode == Keys.ShiftKey) 91 { 92 EnterPlacementMode(); 93 } 94 } 95 96 private void OverlaysTool_KeyUp(object sender, KeyEventArgs e) 97 { 98 if (e.KeyCode == Keys.ShiftKey) 99 { 100 ExitPlacementMode(); 101 } 102 } 103 104 private void MapPanel_MouseDown(object sender, MouseEventArgs e) 105 { 106 if (placementMode) 107 { 108 if (e.Button == MouseButtons.Left) 109 { 110 AddOverlay(navigationWidget.MouseCell); 111 } 112 else if (e.Button == MouseButtons.Right) 113 { 114 RemoveOverlay(navigationWidget.MouseCell); 115 } 116 } 117 else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right)) 118 { 119 PickOverlay(navigationWidget.MouseCell); 120 } 121 } 122 123 private void MapPanel_MouseMove(object sender, MouseEventArgs e) 124 { 125 if (!placementMode && (Control.ModifierKeys == Keys.Shift)) 126 { 127 EnterPlacementMode(); 128 } 129 else if (placementMode && (Control.ModifierKeys == Keys.None)) 130 { 131 ExitPlacementMode(); 132 } 133 } 134 135 private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e) 136 { 137 if (placementMode) 138 { 139 if (SelectedOverlayType != null) 140 { 141 mapPanel.Invalidate(map, new Rectangle(e.OldCell, new Size(1, 1))); 142 mapPanel.Invalidate(map, new Rectangle(e.NewCell, new Size(1, 1))); 143 } 144 } 145 } 146 147 private void AddOverlay(Point location) 148 { 149 if ((location.Y == 0) || (location.Y == (map.Metrics.Height - 1))) 150 { 151 return; 152 } 153 154 if (map.Overlay[location] == null) 155 { 156 if (SelectedOverlayType != null) 157 { 158 var overlay = new Overlay 159 { 160 Type = SelectedOverlayType, 161 Icon = 0 162 }; 163 map.Overlay[location] = overlay; 164 mapPanel.Invalidate(map, location); 165 166 void undoAction(UndoRedoEventArgs e) 167 { 168 e.MapPanel.Invalidate(e.Map, location); 169 e.Map.Overlay[location] = null; 170 } 171 172 void redoAction(UndoRedoEventArgs e) 173 { 174 e.Map.Overlay[location] = overlay; 175 e.MapPanel.Invalidate(e.Map, location); 176 } 177 178 url.Track(undoAction, redoAction); 179 180 plugin.Dirty = true; 181 } 182 } 183 } 184 185 private void RemoveOverlay(Point location) 186 { 187 if ((map.Overlay[location] is Overlay overlay) && overlay.Type.IsPlaceable) 188 { 189 map.Overlay[location] = null; 190 mapPanel.Invalidate(map, location); 191 192 void undoAction(UndoRedoEventArgs e) 193 { 194 e.Map.Overlay[location] = overlay; 195 e.MapPanel.Invalidate(e.Map, location); 196 } 197 198 void redoAction(UndoRedoEventArgs e) 199 { 200 e.MapPanel.Invalidate(e.Map, location); 201 e.Map.Overlay[location] = null; 202 } 203 204 url.Track(undoAction, redoAction); 205 206 plugin.Dirty = true; 207 } 208 } 209 210 private void EnterPlacementMode() 211 { 212 if (placementMode) 213 { 214 return; 215 } 216 217 placementMode = true; 218 219 navigationWidget.MouseoverSize = Size.Empty; 220 221 if (SelectedOverlayType != null) 222 { 223 mapPanel.Invalidate(map, navigationWidget.MouseCell); 224 } 225 226 UpdateStatus(); 227 } 228 229 private void ExitPlacementMode() 230 { 231 if (!placementMode) 232 { 233 return; 234 } 235 236 placementMode = false; 237 238 navigationWidget.MouseoverSize = new Size(1, 1); 239 240 if (SelectedOverlayType != null) 241 { 242 mapPanel.Invalidate(map, navigationWidget.MouseCell); 243 } 244 245 UpdateStatus(); 246 } 247 248 private void PickOverlay(Point location) 249 { 250 if (map.Metrics.GetCell(location, out int cell)) 251 { 252 var overlay = map.Overlay[cell]; 253 if ((overlay != null) && !overlay.Type.IsWall) 254 { 255 SelectedOverlayType = overlay.Type; 256 } 257 } 258 } 259 260 private void RefreshMapPanel() 261 { 262 overlayTypeMapPanel.MapImage = SelectedOverlayType?.Thumbnail; 263 } 264 265 private void UpdateStatus() 266 { 267 if (placementMode) 268 { 269 statusLbl.Text = "Left-Click to place overlay, Right-Click to remove overlay"; 270 } 271 else 272 { 273 statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick overlay"; 274 } 275 } 276 277 protected override void PreRenderMap() 278 { 279 base.PreRenderMap(); 280 281 previewMap = map.Clone(); 282 if (placementMode) 283 { 284 var location = navigationWidget.MouseCell; 285 if (SelectedOverlayType != null) 286 { 287 if (previewMap.Metrics.GetCell(location, out int cell)) 288 { 289 if (previewMap.Overlay[cell] == null) 290 { 291 previewMap.Overlay[cell] = new Overlay 292 { 293 Type = SelectedOverlayType, 294 Icon = 0, 295 Tint = Color.FromArgb(128, Color.White) 296 }; 297 } 298 } 299 } 300 } 301 } 302 303 protected override void PostRenderMap(Graphics graphics) 304 { 305 base.PostRenderMap(graphics); 306 307 var overlayPen = new Pen(Color.Green, 4.0f); 308 foreach (var (cell, overlay) in previewMap.Overlay.Where(x => x.Value.Type.IsPlaceable)) 309 { 310 previewMap.Metrics.GetLocation(cell, out Point topLeft); 311 var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize); 312 graphics.DrawRectangle(overlayPen, bounds); 313 } 314 } 315 316 #region IDisposable Support 317 private bool disposedValue = false; 318 319 protected override void Dispose(bool disposing) 320 { 321 if (!disposedValue) 322 { 323 if (disposing) 324 { 325 overlayTypeComboBox.SelectedIndexChanged -= OverlayTypeComboBox_SelectedIndexChanged; 326 327 mapPanel.MouseDown -= MapPanel_MouseDown; 328 mapPanel.MouseMove -= MapPanel_MouseMove; 329 (mapPanel as Control).KeyDown -= OverlaysTool_KeyDown; 330 (mapPanel as Control).KeyUp -= OverlaysTool_KeyUp; 331 332 navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged; 333 } 334 disposedValue = true; 335 } 336 337 base.Dispose(disposing); 338 } 339 #endregion 340 } 341 }