CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

WallsTool.cs (13179B)


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