CnC_Remastered_Collection

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

UnitTool.cs (14343B)


      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.Render;
     20 using MobiusEditor.Utility;
     21 using MobiusEditor.Widgets;
     22 using System;
     23 using System.ComponentModel;
     24 using System.Drawing;
     25 using System.Linq;
     26 using System.Windows.Forms;
     27 
     28 namespace MobiusEditor.Tools
     29 {
     30     public class UnitTool : ViewTool
     31     {
     32         private readonly TypeComboBox unitTypeComboBox;
     33         private readonly MapPanel unitTypeMapPanel;
     34         private readonly ObjectProperties objectProperties;
     35 
     36         private Map previewMap;
     37         protected override Map RenderMap => previewMap;
     38 
     39         private bool placementMode;
     40 
     41         private readonly Unit mockUnit;
     42 
     43         private Unit selectedUnit;
     44         private ObjectPropertiesPopup selectedObjectProperties;
     45 
     46         private UnitType selectedUnitType;
     47         private UnitType SelectedUnitType
     48         {
     49             get => selectedUnitType;
     50             set
     51             {
     52                 if (selectedUnitType != value)
     53                 {
     54                     if (placementMode && (selectedUnitType != null))
     55                     {
     56                         mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
     57                     }
     58 
     59                     selectedUnitType = value;
     60                     unitTypeComboBox.SelectedValue = selectedUnitType;
     61 
     62                     if (placementMode && (selectedUnitType != null))
     63                     {
     64                         mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
     65                     }
     66 
     67                     mockUnit.Type = selectedUnitType;
     68 
     69                     RefreshMapPanel();
     70                 }
     71             }
     72         }
     73 
     74         public UnitTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox unitTypeComboBox, MapPanel unitTypeMapPanel, ObjectProperties objectProperties, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
     75             : base(mapPanel, layers, statusLbl, plugin, url)
     76         {
     77             previewMap = map;
     78 
     79             mockUnit = new Unit()
     80             {
     81                 Type = unitTypeComboBox.Types.First() as UnitType,
     82                 House = map.Houses.First().Type,
     83                 Strength = 256,
     84                 Direction = map.DirectionTypes.Where(d => d.Equals(FacingType.North)).First(),
     85                 Mission = map.MissionTypes.Where(m => m.Equals("Guard")).FirstOrDefault() ?? map.MissionTypes.First()
     86             };
     87             mockUnit.PropertyChanged += MockUnit_PropertyChanged;
     88 
     89             this.mapPanel.MouseDown += MapPanel_MouseDown;
     90             this.mapPanel.MouseUp += MapPanel_MouseUp;
     91             this.mapPanel.MouseDoubleClick += MapPanel_MouseDoubleClick;
     92             this.mapPanel.MouseMove += MapPanel_MouseMove;
     93             (this.mapPanel as Control).KeyDown += UnitTool_KeyDown;
     94             (this.mapPanel as Control).KeyUp += UnitTool_KeyUp;
     95 
     96             this.unitTypeComboBox = unitTypeComboBox;
     97             this.unitTypeComboBox.SelectedIndexChanged += UnitTypeComboBox_SelectedIndexChanged;
     98 
     99             this.unitTypeMapPanel = unitTypeMapPanel;
    100             this.unitTypeMapPanel.BackColor = Color.White;
    101             this.unitTypeMapPanel.MaxZoom = 1;
    102 
    103             this.objectProperties = objectProperties;
    104             this.objectProperties.Object = mockUnit;
    105 
    106             navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
    107 
    108             SelectedUnitType = mockUnit.Type;
    109 
    110             UpdateStatus();
    111         }
    112 
    113         private void MapPanel_MouseDoubleClick(object sender, MouseEventArgs e)
    114         {
    115             if (Control.ModifierKeys != Keys.None)
    116             {
    117                 return;
    118             }
    119 
    120             if (map.Metrics.GetCell(navigationWidget.MouseCell, out int cell))
    121             {
    122                 if (map.Technos[cell] is Unit unit)
    123                 {
    124                     selectedUnit = null;
    125 
    126                     selectedObjectProperties?.Close();
    127                     selectedObjectProperties = new ObjectPropertiesPopup(objectProperties.Plugin, unit);
    128                     selectedObjectProperties.Closed += (cs, ce) =>
    129                     {
    130                         navigationWidget.Refresh();
    131                     };
    132 
    133                     unit.PropertyChanged += SelectedUnit_PropertyChanged;
    134 
    135                     selectedObjectProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
    136 
    137                     UpdateStatus();
    138                 }
    139             }
    140         }
    141 
    142         private void MockUnit_PropertyChanged(object sender, PropertyChangedEventArgs e)
    143         {
    144             RefreshMapPanel();
    145         }
    146 
    147         private void SelectedUnit_PropertyChanged(object sender, PropertyChangedEventArgs e)
    148         {
    149             mapPanel.Invalidate(map, sender as Unit);
    150         }
    151 
    152         private void UnitTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
    153         {
    154             SelectedUnitType = unitTypeComboBox.SelectedValue as UnitType;
    155         }
    156 
    157         private void UnitTool_KeyDown(object sender, KeyEventArgs e)
    158         {
    159             if (e.KeyCode == Keys.ShiftKey)
    160             {
    161                 EnterPlacementMode();
    162             }
    163         }
    164 
    165         private void UnitTool_KeyUp(object sender, KeyEventArgs e)
    166         {
    167             if (e.KeyCode == Keys.ShiftKey)
    168             {
    169                 ExitPlacementMode();
    170             }
    171         }
    172 
    173         private void MapPanel_MouseMove(object sender, MouseEventArgs e)
    174         {
    175             if (!placementMode && (Control.ModifierKeys == Keys.Shift))
    176             {
    177                 EnterPlacementMode();
    178             }
    179             else if (placementMode && (Control.ModifierKeys == Keys.None))
    180             {
    181                 ExitPlacementMode();
    182             }
    183         }
    184 
    185         private void MapPanel_MouseDown(object sender, MouseEventArgs e)
    186         {
    187             if (placementMode)
    188             {
    189                 if (e.Button == MouseButtons.Left)
    190                 {
    191                     AddUnit(navigationWidget.MouseCell);
    192                 }
    193                 else if (e.Button == MouseButtons.Right)
    194                 {
    195                     RemoveUnit(navigationWidget.MouseCell);
    196                 }
    197             }
    198             else if (e.Button == MouseButtons.Left)
    199             {
    200                 SelectUnit(navigationWidget.MouseCell);
    201             }
    202             else if (e.Button == MouseButtons.Right)
    203             {
    204                 PickUnit(navigationWidget.MouseCell);
    205             }
    206         }
    207 
    208         private void MapPanel_MouseUp(object sender, MouseEventArgs e)
    209         {
    210             if (selectedUnit != null)
    211             {
    212                 selectedUnit = null;
    213 
    214                 UpdateStatus();
    215             }
    216         }
    217 
    218         private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
    219         {
    220             if (placementMode)
    221             {
    222                 if (SelectedUnitType != null)
    223                 {
    224                     mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.OldCell, new Size(1, 1)), 1, 1));
    225                     mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.NewCell, new Size(1, 1)), 1, 1));
    226                 }
    227             }
    228             else if (selectedUnit != null)
    229             {
    230                 var oldLocation = map.Technos[selectedUnit].Value;
    231                 mapPanel.Invalidate(map, selectedUnit);
    232                 map.Technos.Remove(selectedUnit);
    233                 if (map.Technos.Add(e.NewCell, selectedUnit))
    234                 {
    235                     mapPanel.Invalidate(map, selectedUnit);
    236                     plugin.Dirty = true;
    237                 }
    238                 else
    239                 {
    240                     map.Technos.Add(oldLocation, selectedUnit);
    241                 }
    242             }
    243         }
    244 
    245         private void AddUnit(Point location)
    246         {
    247             if (SelectedUnitType != null)
    248             {
    249                 var unit = mockUnit.Clone();
    250                 if (map.Technos.Add(location, unit))
    251                 {
    252                     mapPanel.Invalidate(map, unit);
    253                     plugin.Dirty = true;
    254                 }
    255             }
    256         }
    257 
    258         private void RemoveUnit(Point location)
    259         {
    260             if (map.Technos[location] is Unit unit)
    261             {
    262                 mapPanel.Invalidate(map, unit);
    263                 map.Technos.Remove(unit);
    264                 plugin.Dirty = true;
    265             }
    266         }
    267 
    268         private void EnterPlacementMode()
    269         {
    270             if (placementMode)
    271             {
    272                 return;
    273             }
    274 
    275             placementMode = true;
    276 
    277             navigationWidget.MouseoverSize = Size.Empty;
    278 
    279             if (SelectedUnitType != null)
    280             {
    281                 mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
    282             }
    283 
    284             UpdateStatus();
    285         }
    286 
    287         private void ExitPlacementMode()
    288         {
    289             if (!placementMode)
    290             {
    291                 return;
    292             }
    293 
    294             placementMode = false;
    295 
    296             navigationWidget.MouseoverSize = new Size(1, 1);
    297 
    298             if (SelectedUnitType != null)
    299             {
    300                 mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
    301             }
    302 
    303             UpdateStatus();
    304         }
    305 
    306         private void PickUnit(Point location)
    307         {
    308             if (map.Metrics.GetCell(location, out int cell))
    309             {
    310                 if (map.Technos[cell] is Unit unit)
    311                 {
    312                     SelectedUnitType = unit.Type;
    313                     mockUnit.House = unit.House;
    314                     mockUnit.Strength = unit.Strength;
    315                     mockUnit.Direction = unit.Direction;
    316                     mockUnit.Mission = unit.Mission;
    317                     mockUnit.Trigger = unit.Trigger;
    318                 }
    319             }
    320         }
    321 
    322         private void SelectUnit(Point location)
    323         {
    324             if (map.Metrics.GetCell(location, out int cell))
    325             {
    326                 selectedUnit = map.Technos[cell] as Unit;
    327             }
    328 
    329             UpdateStatus();
    330         }
    331 
    332         private void RefreshMapPanel()
    333         {
    334             if (mockUnit.Type != null)
    335             {
    336                 var unitPreview = new Bitmap(Globals.TileWidth * 3, Globals.TileHeight * 3);
    337                 using (var g = Graphics.FromImage(unitPreview))
    338                 {
    339                     MapRenderer.Render(plugin.GameType, map.Theater, new Point(1, 1), Globals.TileSize, mockUnit).Item2(g);
    340                 }
    341                 unitTypeMapPanel.MapImage = unitPreview;
    342             }
    343             else
    344             {
    345                 unitTypeMapPanel.MapImage = null;
    346             }
    347         }
    348 
    349         private void UpdateStatus()
    350         {
    351             if (placementMode)
    352             {
    353                 statusLbl.Text = "Left-Click to place unit, Right-Click to remove unit";
    354             }
    355             else if (selectedUnit != null)
    356             {
    357                 statusLbl.Text = "Drag mouse to move unit";
    358             }
    359             else
    360             {
    361                 statusLbl.Text = "Shift to enter placement mode, Left-Click drag to move unit, Double-Click update unit properties, Right-Click to pick unit";
    362             }
    363         }
    364 
    365         protected override void PreRenderMap()
    366         {
    367             base.PreRenderMap();
    368 
    369             previewMap = map.Clone();
    370             if (placementMode)
    371             {
    372                 var location = navigationWidget.MouseCell;
    373                 if (SelectedUnitType != null)
    374                 {
    375                     var unit = mockUnit.Clone();
    376                     unit.Tint = Color.FromArgb(128, Color.White);
    377                     if (previewMap.Technos.Add(location, unit))
    378                     {
    379                         mapPanel.Invalidate(previewMap, unit);
    380                     }
    381                 }
    382             }
    383         }
    384 
    385         protected override void PostRenderMap(Graphics graphics)
    386         {
    387             base.PostRenderMap(graphics);
    388 
    389             var unitPen = new Pen(Color.Green, 4.0f);
    390             foreach (var (topLeft, _) in map.Technos.OfType<Unit>())
    391             {
    392                 var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize);
    393                 graphics.DrawRectangle(unitPen, bounds);
    394             }
    395         }
    396 
    397         #region IDisposable Support
    398         private bool disposedValue = false;
    399 
    400         protected override void Dispose(bool disposing)
    401         {
    402             if (!disposedValue)
    403             {
    404                 if (disposing)
    405                 {
    406                     selectedObjectProperties?.Close();
    407 
    408                     mapPanel.MouseDown -= MapPanel_MouseDown;
    409                     mapPanel.MouseUp -= MapPanel_MouseUp;
    410                     mapPanel.MouseDoubleClick -= MapPanel_MouseDoubleClick;
    411                     mapPanel.MouseMove -= MapPanel_MouseMove;
    412                     (mapPanel as Control).KeyDown -= UnitTool_KeyDown;
    413                     (mapPanel as Control).KeyUp -= UnitTool_KeyUp;
    414 
    415                     unitTypeComboBox.SelectedIndexChanged -= UnitTypeComboBox_SelectedIndexChanged;
    416 
    417                     navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
    418                 }
    419                 disposedValue = true;
    420             }
    421 
    422             base.Dispose(disposing);
    423         }
    424         #endregion
    425     }
    426 }