CnC_Remastered_Collection

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

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 }