C&C Remastered Map Editor

Initial commit of C&C Remastered Map Editor code
This commit is contained in:
PG-SteveT 2020-09-10 11:12:58 -07:00
parent 1f6350fe6e
commit e37e174be1
289 changed files with 80922 additions and 7 deletions

View file

@ -0,0 +1,502 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Render;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class BuildingTool : ViewTool
{
private readonly TypeComboBox buildingTypeComboBox;
private readonly MapPanel buildingTypeMapPanel;
private readonly ObjectProperties objectProperties;
private Map previewMap;
protected override Map RenderMap => previewMap;
private bool placementMode;
private readonly Building mockBuilding;
private Building selectedBuilding;
private ObjectPropertiesPopup selectedObjectProperties;
private Point selectedBuildingPivot;
private BuildingType selectedBuildingType;
private BuildingType SelectedBuildingType
{
get => selectedBuildingType;
set
{
if (selectedBuildingType != value)
{
if (placementMode && (selectedBuildingType != null))
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, selectedBuildingType.OverlapBounds.Size));
}
selectedBuildingType = value;
buildingTypeComboBox.SelectedValue = selectedBuildingType;
if (placementMode && (selectedBuildingType != null))
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, selectedBuildingType.OverlapBounds.Size));
}
mockBuilding.Type = selectedBuildingType;
RefreshMapPanel();
}
}
}
public BuildingTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox buildingTypeComboBox, MapPanel buildingTypeMapPanel, ObjectProperties objectProperties, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
previewMap = map;
mockBuilding = new Building()
{
Type = buildingTypeComboBox.Types.First() as BuildingType,
House = map.Houses.First().Type,
Strength = 256,
Direction = map.DirectionTypes.Where(d => d.Equals(FacingType.North)).First()
};
mockBuilding.PropertyChanged += MockBuilding_PropertyChanged;
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseUp += MapPanel_MouseUp;
this.mapPanel.MouseDoubleClick += MapPanel_MouseDoubleClick;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += UnitTool_KeyDown;
(this.mapPanel as Control).KeyUp += UnitTool_KeyUp;
this.buildingTypeComboBox = buildingTypeComboBox;
this.buildingTypeComboBox.SelectedIndexChanged += UnitTypeComboBox_SelectedIndexChanged;
this.buildingTypeMapPanel = buildingTypeMapPanel;
this.buildingTypeMapPanel.BackColor = Color.White;
this.buildingTypeMapPanel.MaxZoom = 1;
this.objectProperties = objectProperties;
this.objectProperties.Object = mockBuilding;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
SelectedBuildingType = mockBuilding.Type;
UpdateStatus();
}
private void MapPanel_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (Control.ModifierKeys != Keys.None)
{
return;
}
if (map.Metrics.GetCell(navigationWidget.MouseCell, out int cell))
{
if (map.Technos[cell] is Building building)
{
selectedBuilding = null;
selectedBuildingPivot = Point.Empty;
selectedObjectProperties?.Close();
selectedObjectProperties = new ObjectPropertiesPopup(objectProperties.Plugin, building);
selectedObjectProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
};
building.PropertyChanged += SelectedBuilding_PropertyChanged;
selectedObjectProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
UpdateStatus();
}
}
}
private void MockBuilding_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if ((mockBuilding.Type == null) || !mockBuilding.Type.HasTurret)
{
mockBuilding.Direction = map.DirectionTypes.Where(d => d.Equals(FacingType.North)).First();
}
RefreshMapPanel();
}
private void SelectedBuilding_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
mapPanel.Invalidate(map, sender as Building);
}
private void UnitTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedBuildingType = buildingTypeComboBox.SelectedValue as BuildingType;
}
private void UnitTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void UnitTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
AddBuilding(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveBuilding(navigationWidget.MouseCell);
}
}
else if (e.Button == MouseButtons.Left)
{
SelectBuilding(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
PickBuilding(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if (selectedBuilding != null)
{
selectedBuilding = null;
selectedBuildingPivot = Point.Empty;
UpdateStatus();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (SelectedBuildingType != null)
{
mapPanel.Invalidate(map, new Rectangle(e.OldCell, SelectedBuildingType.OverlapBounds.Size));
mapPanel.Invalidate(map, new Rectangle(e.NewCell, SelectedBuildingType.OverlapBounds.Size));
}
}
else if (selectedBuilding != null)
{
var oldLocation = map.Technos[selectedBuilding].Value;
var newLocation = new Point(Math.Max(0, e.NewCell.X - selectedBuildingPivot.X), Math.Max(0, e.NewCell.Y - selectedBuildingPivot.Y));
mapPanel.Invalidate(map, selectedBuilding);
map.Buildings.Remove(selectedBuilding);
if (map.Technos.CanAdd(newLocation, selectedBuilding, selectedBuilding.Type.BaseOccupyMask) &&
map.Buildings.Add(newLocation, selectedBuilding))
{
mapPanel.Invalidate(map, selectedBuilding);
plugin.Dirty = true;
}
else
{
map.Buildings.Add(oldLocation, selectedBuilding);
}
}
}
private void AddBuilding(Point location)
{
if (SelectedBuildingType != null)
{
var building = mockBuilding.Clone();
if (map.Technos.CanAdd(location, building, building.Type.BaseOccupyMask) && map.Buildings.Add(location, building))
{
if (building.BasePriority >= 0)
{
foreach (var baseBuilding in map.Buildings.OfType<Building>().Select(x => x.Occupier).Where(x => x.BasePriority >= 0))
{
if ((building != baseBuilding) && (baseBuilding.BasePriority >= building.BasePriority))
{
baseBuilding.BasePriority++;
}
}
var baseBuildings = map.Buildings.OfType<Building>().Select(x => x.Occupier).Where(x => x.BasePriority >= 0).OrderBy(x => x.BasePriority).ToArray();
for (var i = 0; i < baseBuildings.Length; ++i)
{
baseBuildings[i].BasePriority = i;
}
foreach (var baseBuilding in map.Buildings.OfType<Building>().Select(x => x.Occupier).Where(x => x.BasePriority >= 0))
{
mapPanel.Invalidate(map, baseBuilding);
}
}
mapPanel.Invalidate(map, building);
plugin.Dirty = true;
}
}
}
private void RemoveBuilding(Point location)
{
if (map.Technos[location] is Building building)
{
mapPanel.Invalidate(map, building);
map.Buildings.Remove(building);
if (building.BasePriority >= 0)
{
var baseBuildings = map.Buildings.OfType<Building>().Select(x => x.Occupier).Where(x => x.BasePriority >= 0).OrderBy(x => x.BasePriority).ToArray();
for (var i = 0; i < baseBuildings.Length; ++i)
{
baseBuildings[i].BasePriority = i;
}
foreach (var baseBuilding in map.Buildings.OfType<Building>().Select(x => x.Occupier).Where(x => x.BasePriority >= 0))
{
mapPanel.Invalidate(map, baseBuilding);
}
}
plugin.Dirty = true;
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedBuildingType != null)
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, SelectedBuildingType.OverlapBounds.Size));
}
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedBuildingType != null)
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, SelectedBuildingType.OverlapBounds.Size));
}
UpdateStatus();
}
private void PickBuilding(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
if (map.Technos[cell] is Building building)
{
SelectedBuildingType = building.Type;
mockBuilding.House = building.House;
mockBuilding.Strength = building.Strength;
mockBuilding.Direction = building.Direction;
mockBuilding.Trigger = building.Trigger;
mockBuilding.BasePriority = building.BasePriority;
mockBuilding.IsPrebuilt = building.IsPrebuilt;
mockBuilding.Sellable = building.Sellable;
mockBuilding.Rebuild = building.Rebuild;
}
}
}
private void SelectBuilding(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
selectedBuilding = map.Technos[cell] as Building;
selectedBuildingPivot = (selectedBuilding != null) ? (location - (Size)map.Technos[selectedBuilding].Value) : Point.Empty;
}
UpdateStatus();
}
private void RefreshMapPanel()
{
if (mockBuilding.Type != null)
{
var render = MapRenderer.Render(plugin.GameType, map.Theater, new Point(0, 0), Globals.TileSize, Globals.TileScale, mockBuilding);
if (!render.Item1.IsEmpty)
{
var buildingPreview = new Bitmap(render.Item1.Width, render.Item1.Height);
using (var g = Graphics.FromImage(buildingPreview))
{
render.Item2(g);
}
buildingTypeMapPanel.MapImage = buildingPreview;
}
else
{
buildingTypeMapPanel.MapImage = null;
}
}
else
{
buildingTypeMapPanel.MapImage = null;
}
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to place building, Right-Click to remove building";
}
else if (selectedBuilding != null)
{
statusLbl.Text = "Drag mouse to move building";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click drag to move building, Double-Click update building properties, Right-Click to pick building";
}
}
protected override void PreRenderMap()
{
base.PreRenderMap();
previewMap = map.Clone();
if (placementMode)
{
var location = navigationWidget.MouseCell;
if (SelectedBuildingType != null)
{
var building = mockBuilding.Clone();
building.Tint = Color.FromArgb(128, Color.White);
if (previewMap.Technos.CanAdd(location, building, building.Type.BaseOccupyMask) && previewMap.Buildings.Add(location, building))
{
mapPanel.Invalidate(previewMap, building);
}
}
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var buildingPen = new Pen(Color.Green, 4.0f);
var occupyPen = new Pen(Color.Red, 2.0f);
foreach (var (topLeft, building) in map.Buildings.OfType<Building>())
{
var bounds = new Rectangle(
new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight),
new Size(building.Type.Size.Width * Globals.TileWidth, building.Type.Size.Height * Globals.TileHeight)
);
graphics.DrawRectangle(buildingPen, bounds);
for (var y = 0; y < building.Type.BaseOccupyMask.GetLength(0); ++y)
{
for (var x = 0; x < building.Type.BaseOccupyMask.GetLength(1); ++x)
{
if (building.Type.BaseOccupyMask[y, x])
{
var occupyBounds = new Rectangle(
new Point((topLeft.X + x) * Globals.TileWidth, (topLeft.Y + y) * Globals.TileHeight),
Globals.TileSize
);
graphics.DrawRectangle(occupyPen, occupyBounds);
}
}
}
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseUp -= MapPanel_MouseUp;
mapPanel.MouseDoubleClick -= MapPanel_MouseDoubleClick;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= UnitTool_KeyDown;
(mapPanel as Control).KeyUp -= UnitTool_KeyUp;
buildingTypeComboBox.SelectedIndexChanged -= UnitTypeComboBox_SelectedIndexChanged;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,268 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class CellTriggersTool : ViewTool
{
private readonly ComboBox triggerCombo;
private readonly Dictionary<int, CellTrigger> undoCellTriggers = new Dictionary<int, CellTrigger>();
private readonly Dictionary<int, CellTrigger> redoCellTriggers = new Dictionary<int, CellTrigger>();
private bool placementMode;
public CellTriggersTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, ComboBox triggerCombo, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseUp += MapPanel_MouseUp;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += WaypointsTool_KeyDown;
(this.mapPanel as Control).KeyUp += WaypointsTool_KeyUp;
this.triggerCombo = triggerCombo;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
UpdateStatus();
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
SetCellTrigger(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveCellTrigger(navigationWidget.MouseCell);
}
}
else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right))
{
PickCellTrigger(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if ((undoCellTriggers.Count > 0) || (redoCellTriggers.Count > 0))
{
CommitChange();
}
}
private void WaypointsTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void WaypointsTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (Control.MouseButtons == MouseButtons.Left)
{
SetCellTrigger(e.NewCell);
}
else if (Control.MouseButtons == MouseButtons.Right)
{
RemoveCellTrigger(e.NewCell);
}
}
}
private void SetCellTrigger(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
if (map.CellTriggers[cell] == null)
{
if (!undoCellTriggers.ContainsKey(cell))
{
undoCellTriggers[cell] = map.CellTriggers[cell];
}
var cellTrigger = new CellTrigger { Trigger = triggerCombo.SelectedItem as string };
map.CellTriggers[cell] = cellTrigger;
redoCellTriggers[cell] = cellTrigger;
mapPanel.Invalidate();
plugin.Dirty = true;
}
}
}
private void RemoveCellTrigger(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var cellTrigger = map.CellTriggers[cell];
if (cellTrigger != null)
{
if (!undoCellTriggers.ContainsKey(cell))
{
undoCellTriggers[cell] = map.CellTriggers[cell];
}
map.CellTriggers[cell] = null;
redoCellTriggers[cell] = null;
mapPanel.Invalidate();
plugin.Dirty = true;
}
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
UpdateStatus();
}
private void PickCellTrigger(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var cellTrigger = map.CellTriggers[cell];
if (cellTrigger != null)
{
triggerCombo.SelectedItem = cellTrigger.Trigger;
}
}
}
private void CommitChange()
{
var undoCellTriggers2 = new Dictionary<int, CellTrigger>(undoCellTriggers);
void undoAction(UndoRedoEventArgs e)
{
foreach (var kv in undoCellTriggers2)
{
e.Map.CellTriggers[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate();
}
var redoCellTriggers2 = new Dictionary<int, CellTrigger>(redoCellTriggers);
void redoAction(UndoRedoEventArgs e)
{
foreach (var kv in redoCellTriggers2)
{
e.Map.CellTriggers[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate();
}
undoCellTriggers.Clear();
redoCellTriggers.Clear();
url.Track(undoAction, redoAction);
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to set cell trigger, Right-Click to clear cell trigger";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick cell trigger";
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseUp -= MapPanel_MouseUp;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= WaypointsTool_KeyDown;
(mapPanel as Control).KeyUp -= WaypointsTool_KeyUp;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,123 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
namespace MobiusEditor.Tools.Dialogs
{
partial class CellTriggersToolDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
this.label1 = new System.Windows.Forms.Label();
this.triggerCombo = new System.Windows.Forms.ComboBox();
this.tableLayoutPanel4.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel4
//
this.tableLayoutPanel4.AutoSize = true;
this.tableLayoutPanel4.ColumnCount = 2;
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel4.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel4.Controls.Add(this.triggerCombo, 1, 0);
this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
this.tableLayoutPanel4.RowCount = 2;
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.Size = new System.Drawing.Size(218, 118);
this.tableLayoutPanel4.TabIndex = 3;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.Location = new System.Drawing.Point(4, 0);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(58, 38);
this.label1.TabIndex = 0;
this.label1.Text = "Trigger";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// triggerCombo
//
this.triggerCombo.Dock = System.Windows.Forms.DockStyle.Fill;
this.triggerCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.triggerCombo.FormattingEnabled = true;
this.triggerCombo.Location = new System.Drawing.Point(70, 5);
this.triggerCombo.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.triggerCombo.Name = "triggerCombo";
this.triggerCombo.Size = new System.Drawing.Size(144, 28);
this.triggerCombo.TabIndex = 1;
//
// CellTriggersToolDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(218, 118);
this.ControlBox = false;
this.Controls.Add(this.tableLayoutPanel4);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(210, 91);
this.Name = "CellTriggersToolDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Cell Triggers";
this.tableLayoutPanel4.ResumeLayout(false);
this.tableLayoutPanel4.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ComboBox triggerCombo;
}
}

View file

@ -0,0 +1,39 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using System;
using System.Windows.Forms;
namespace MobiusEditor.Tools.Dialogs
{
public partial class CellTriggersToolDialog : Form
{
public ComboBox TriggerCombo => triggerCombo;
public CellTriggersToolDialog()
{
InitializeComponent();
Location = Properties.Settings.Default.ToolDialogPosition;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.ToolDialogPosition = Location;
Properties.Settings.Default.Save();
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,121 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
namespace MobiusEditor.Tools.Dialogs
{
partial class GenericToolDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TemplateToolDialog));
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.genericTypeMapPanel = new MobiusEditor.Controls.MapPanel();
this.genericTypeComboBox = new MobiusEditor.Controls.TypeComboBox();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.genericTypeMapPanel, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.genericTypeComboBox, 0, 0);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 2;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(274, 254);
this.tableLayoutPanel1.TabIndex = 0;
//
// templateTypeMapPanel
//
this.genericTypeMapPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.genericTypeMapPanel.Location = new System.Drawing.Point(3, 30);
this.genericTypeMapPanel.MaxZoom = 8;
this.genericTypeMapPanel.MinZoom = 1;
this.genericTypeMapPanel.Name = "templateTypeMapPanel";
this.genericTypeMapPanel.Quality = 1;
this.genericTypeMapPanel.Size = new System.Drawing.Size(268, 221);
this.genericTypeMapPanel.TabIndex = 3;
this.genericTypeMapPanel.Zoom = 1;
//
// templateTypeComboBox
//
this.genericTypeComboBox.DisplayMember = "Name";
this.genericTypeComboBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.genericTypeComboBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.genericTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.genericTypeComboBox.FormattingEnabled = true;
this.genericTypeComboBox.Location = new System.Drawing.Point(3, 3);
this.genericTypeComboBox.MissingThumbnail = ((System.Drawing.Image)(resources.GetObject("templateTypeComboBox.MissingThumbnail")));
this.genericTypeComboBox.Name = "templateTypeComboBox";
this.genericTypeComboBox.Size = new System.Drawing.Size(268, 21);
this.genericTypeComboBox.TabIndex = 2;
this.genericTypeComboBox.ValueMember = "Type";
//
// TemplateToolDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(274, 254);
this.ControlBox = false;
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(290, 293);
this.Name = "TemplateToolDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Map";
this.tableLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private Controls.TypeComboBox genericTypeComboBox;
private Controls.MapPanel genericTypeMapPanel;
}
}

View file

@ -0,0 +1,49 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MobiusEditor.Tools.Dialogs
{
public partial class GenericToolDialog : Form
{
public TypeComboBox GenericTypeComboBox => genericTypeComboBox;
public MapPanel GenericTypeMapPanel => genericTypeMapPanel;
public GenericToolDialog()
{
InitializeComponent();
Location = Properties.Settings.Default.ToolDialogPosition;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.ToolDialogPosition = Location;
Properties.Settings.Default.Save();
}
}
}

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="templateTypeComboBox.MissingThumbnail" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
fwDWYGmBKz70PQAAAABJRU5ErkJggg==
</value>
</data>
</root>

View file

@ -0,0 +1,144 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
namespace MobiusEditor.Tools.Dialogs
{
partial class ObjectToolDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ObjectToolDialog));
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.objectTypeMapPanel = new MobiusEditor.Controls.MapPanel();
this.objectTypeComboBox = new MobiusEditor.Controls.TypeComboBox();
this.objectProperties = new MobiusEditor.Controls.ObjectProperties();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.objectTypeMapPanel, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.objectTypeComboBox, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.objectProperties, 0, 2);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(365, 582);
this.tableLayoutPanel1.TabIndex = 0;
//
// objectTypeMapPanel
//
this.objectTypeMapPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.objectTypeMapPanel.Location = new System.Drawing.Point(4, 35);
this.objectTypeMapPanel.MapImage = null;
this.objectTypeMapPanel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.objectTypeMapPanel.MaxZoom = 8;
this.objectTypeMapPanel.MinZoom = 1;
this.objectTypeMapPanel.Name = "objectTypeMapPanel";
this.objectTypeMapPanel.Quality = 1;
this.objectTypeMapPanel.Size = new System.Drawing.Size(357, 272);
this.objectTypeMapPanel.TabIndex = 3;
this.objectTypeMapPanel.Zoom = 1;
this.objectTypeMapPanel.ZoomStep = 1;
//
// objectTypeComboBox
//
this.objectTypeComboBox.DisplayMember = "Name";
this.objectTypeComboBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.objectTypeComboBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.objectTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.objectTypeComboBox.FormattingEnabled = true;
this.objectTypeComboBox.Location = new System.Drawing.Point(4, 4);
this.objectTypeComboBox.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.objectTypeComboBox.MissingThumbnail = ((System.Drawing.Image)(resources.GetObject("objectTypeComboBox.MissingThumbnail")));
this.objectTypeComboBox.Name = "objectTypeComboBox";
this.objectTypeComboBox.Size = new System.Drawing.Size(357, 23);
this.objectTypeComboBox.TabIndex = 2;
this.objectTypeComboBox.ValueMember = "Type";
//
// objectProperties
//
this.objectProperties.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.objectProperties.Location = new System.Drawing.Point(5, 316);
this.objectProperties.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
this.objectProperties.Name = "objectProperties";
this.objectProperties.Object = null;
this.objectProperties.Size = new System.Drawing.Size(355, 261);
this.objectProperties.TabIndex = 4;
//
// ObjectToolDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(365, 582);
this.ControlBox = false;
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(381, 528);
this.Name = "ObjectToolDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Map";
this.tableLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private Controls.TypeComboBox objectTypeComboBox;
private Controls.MapPanel objectTypeMapPanel;
private Controls.ObjectProperties objectProperties;
}
}

View file

@ -0,0 +1,58 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Interface;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MobiusEditor.Tools.Dialogs
{
public partial class ObjectToolDialog : Form
{
public TypeComboBox ObjectTypeComboBox => objectTypeComboBox;
public MapPanel ObjectTypeMapPanel => objectTypeMapPanel;
public ObjectProperties ObjectProperties => objectProperties;
public ObjectToolDialog()
{
InitializeComponent();
Location = Properties.Settings.Default.ToolDialogPosition;
}
public ObjectToolDialog(IGamePlugin plugin)
: this()
{
objectProperties.Initialize(plugin, true);
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.ToolDialogPosition = Location;
Properties.Settings.Default.Save();
}
}
}

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="objectTypeComboBox.MissingThumbnail" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
fwDWYGmBKz70PQAAAABJRU5ErkJggg==
</value>
</data>
</root>

View file

@ -0,0 +1,179 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
namespace MobiusEditor.Tools.Dialogs
{
partial class ResourcesToolDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tableLayoutPanel6 = new System.Windows.Forms.TableLayoutPanel();
this.label10 = new System.Windows.Forms.Label();
this.totalResourcesLbl = new System.Windows.Forms.Label();
this.gemsCheckBox = new System.Windows.Forms.CheckBox();
this.resourceBrushSizeNud = new System.Windows.Forms.NumericUpDown();
this.label1 = new System.Windows.Forms.Label();
this.tableLayoutPanel6.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.resourceBrushSizeNud)).BeginInit();
this.SuspendLayout();
//
// tableLayoutPanel6
//
this.tableLayoutPanel6.ColumnCount = 2;
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.Controls.Add(this.label10, 0, 0);
this.tableLayoutPanel6.Controls.Add(this.totalResourcesLbl, 1, 0);
this.tableLayoutPanel6.Controls.Add(this.gemsCheckBox, 0, 3);
this.tableLayoutPanel6.Controls.Add(this.resourceBrushSizeNud, 1, 2);
this.tableLayoutPanel6.Controls.Add(this.label1, 0, 2);
this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel6.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel6.Name = "tableLayoutPanel6";
this.tableLayoutPanel6.RowCount = 4;
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel6.Size = new System.Drawing.Size(196, 67);
this.tableLayoutPanel6.TabIndex = 1;
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(3, 0);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(88, 13);
this.label10.TabIndex = 0;
this.label10.Text = "Total Resources:";
//
// totalResourcesLbl
//
this.totalResourcesLbl.AutoSize = true;
this.totalResourcesLbl.Location = new System.Drawing.Point(97, 0);
this.totalResourcesLbl.Name = "totalResourcesLbl";
this.totalResourcesLbl.Size = new System.Drawing.Size(13, 13);
this.totalResourcesLbl.TabIndex = 1;
this.totalResourcesLbl.Text = "0";
//
// gemsCheckBox
//
this.gemsCheckBox.AutoSize = true;
this.tableLayoutPanel6.SetColumnSpan(this.gemsCheckBox, 2);
this.gemsCheckBox.Location = new System.Drawing.Point(3, 42);
this.gemsCheckBox.Name = "gemsCheckBox";
this.gemsCheckBox.Size = new System.Drawing.Size(53, 17);
this.gemsCheckBox.TabIndex = 2;
this.gemsCheckBox.Text = "Gems";
this.gemsCheckBox.UseVisualStyleBackColor = true;
//
// resourceBrushSizeNud
//
this.resourceBrushSizeNud.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.resourceBrushSizeNud.Increment = new decimal(new int[] {
2,
0,
0,
0});
this.resourceBrushSizeNud.Location = new System.Drawing.Point(97, 16);
this.resourceBrushSizeNud.Maximum = new decimal(new int[] {
9,
0,
0,
0});
this.resourceBrushSizeNud.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.resourceBrushSizeNud.Name = "resourceBrushSizeNud";
this.resourceBrushSizeNud.Size = new System.Drawing.Size(99, 20);
this.resourceBrushSizeNud.TabIndex = 3;
this.resourceBrushSizeNud.Value = new decimal(new int[] {
1,
0,
0,
0});
//
// label1
//
this.label1.AutoSize = true;
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.Location = new System.Drawing.Point(3, 13);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(88, 26);
this.label1.TabIndex = 4;
this.label1.Text = "Brush Size";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// ResourcesToolDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(196, 67);
this.ControlBox = false;
this.Controls.Add(this.tableLayoutPanel6);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(212, 106);
this.Name = "ResourcesToolDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Resources";
this.tableLayoutPanel6.ResumeLayout(false);
this.tableLayoutPanel6.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.resourceBrushSizeNud)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.Label label10;
private System.Windows.Forms.Label totalResourcesLbl;
private System.Windows.Forms.CheckBox gemsCheckBox;
private System.Windows.Forms.NumericUpDown resourceBrushSizeNud;
private System.Windows.Forms.Label label1;
}
}

View file

@ -0,0 +1,43 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using System;
using System.Windows.Forms;
namespace MobiusEditor.Tools.Dialogs
{
public partial class ResourcesToolDialog : Form
{
public Label TotalResourcesLbl => totalResourcesLbl;
public NumericUpDown ResourceBrushSizeNud => resourceBrushSizeNud;
public CheckBox GemsCheckBox => gemsCheckBox;
public ResourcesToolDialog()
{
InitializeComponent();
Location = Properties.Settings.Default.ToolDialogPosition;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.ToolDialogPosition = Location;
Properties.Settings.Default.Save();
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,130 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
namespace MobiusEditor.Tools.Dialogs
{
partial class TemplateToolDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.templateTypeListView = new System.Windows.Forms.ListView();
this.templateTypeMapPanel = new MobiusEditor.Controls.MapPanel();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.SuspendLayout();
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(2, 2);
this.splitContainer1.Name = "splitContainer1";
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.templateTypeListView);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.templateTypeMapPanel);
this.splitContainer1.Size = new System.Drawing.Size(270, 545);
this.splitContainer1.SplitterDistance = 275;
this.splitContainer1.TabIndex = 7;
//
// templateTypeListView
//
this.templateTypeListView.Dock = System.Windows.Forms.DockStyle.Fill;
this.templateTypeListView.HideSelection = false;
this.templateTypeListView.Location = new System.Drawing.Point(0, 0);
this.templateTypeListView.Margin = new System.Windows.Forms.Padding(0);
this.templateTypeListView.MultiSelect = false;
this.templateTypeListView.Name = "templateTypeListView";
this.templateTypeListView.Size = new System.Drawing.Size(270, 275);
this.templateTypeListView.TabIndex = 7;
this.templateTypeListView.UseCompatibleStateImageBehavior = false;
//
// templateTypeMapPanel
//
this.templateTypeMapPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.templateTypeMapPanel.Location = new System.Drawing.Point(0, 0);
this.templateTypeMapPanel.MapImage = null;
this.templateTypeMapPanel.Margin = new System.Windows.Forms.Padding(0);
this.templateTypeMapPanel.MaxZoom = 8;
this.templateTypeMapPanel.MinZoom = 1;
this.templateTypeMapPanel.Name = "templateTypeMapPanel";
this.templateTypeMapPanel.Quality = 1;
this.templateTypeMapPanel.Size = new System.Drawing.Size(270, 266);
this.templateTypeMapPanel.TabIndex = 6;
this.templateTypeMapPanel.Zoom = 1;
this.templateTypeMapPanel.ZoomStep = 1;
//
// TemplateToolDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(274, 549);
this.ControlBox = false;
this.Controls.Add(this.splitContainer1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(288, 287);
this.Name = "TemplateToolDialog";
this.Padding = new System.Windows.Forms.Padding(2, 2, 2, 2);
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Map";
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.ListView templateTypeListView;
private Controls.MapPanel templateTypeMapPanel;
}
}

View file

@ -0,0 +1,49 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MobiusEditor.Tools.Dialogs
{
public partial class TemplateToolDialog : Form
{
public ListView TemplateTypeListView => templateTypeListView;
public MapPanel TemplateTypeMapPanel => templateTypeMapPanel;
public TemplateToolDialog()
{
InitializeComponent();
Location = Properties.Settings.Default.ToolDialogPosition;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.ToolDialogPosition = Location;
Properties.Settings.Default.Save();
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,147 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
namespace MobiusEditor.Tools.Dialogs
{
partial class TerrainToolDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TerrainToolDialog));
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.terrainTypeMapPanel = new MobiusEditor.Controls.MapPanel();
this.terrainTypeComboBox = new MobiusEditor.Controls.TypeComboBox();
this.terrainProperties = new MobiusEditor.Controls.TerrainProperties();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.AutoSize = true;
this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Controls.Add(this.terrainTypeMapPanel, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.terrainTypeComboBox, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.terrainProperties, 0, 3);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 4;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(411, 466);
this.tableLayoutPanel1.TabIndex = 0;
//
// terrainTypeMapPanel
//
this.terrainTypeMapPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.terrainTypeMapPanel.Location = new System.Drawing.Point(4, 42);
this.terrainTypeMapPanel.MapImage = null;
this.terrainTypeMapPanel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.terrainTypeMapPanel.MaxZoom = 8;
this.terrainTypeMapPanel.MinZoom = 1;
this.terrainTypeMapPanel.Name = "terrainTypeMapPanel";
this.terrainTypeMapPanel.Quality = 1;
this.terrainTypeMapPanel.Size = new System.Drawing.Size(403, 370);
this.terrainTypeMapPanel.TabIndex = 3;
this.terrainTypeMapPanel.Zoom = 1;
this.terrainTypeMapPanel.ZoomStep = 1;
//
// terrainTypeComboBox
//
this.terrainTypeComboBox.DisplayMember = "Name";
this.terrainTypeComboBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.terrainTypeComboBox.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
this.terrainTypeComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.terrainTypeComboBox.FormattingEnabled = true;
this.terrainTypeComboBox.Location = new System.Drawing.Point(4, 5);
this.terrainTypeComboBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.terrainTypeComboBox.MissingThumbnail = ((System.Drawing.Image)(resources.GetObject("terrainTypeComboBox.MissingThumbnail")));
this.terrainTypeComboBox.Name = "terrainTypeComboBox";
this.terrainTypeComboBox.Size = new System.Drawing.Size(403, 27);
this.terrainTypeComboBox.TabIndex = 2;
this.terrainTypeComboBox.ValueMember = "Type";
//
// terrainProperties
//
this.terrainProperties.Dock = System.Windows.Forms.DockStyle.Top;
this.terrainProperties.Location = new System.Drawing.Point(4, 422);
this.terrainProperties.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.terrainProperties.Name = "terrainProperties";
this.terrainProperties.Size = new System.Drawing.Size(403, 38);
this.terrainProperties.TabIndex = 4;
this.terrainProperties.Terrain = null;
//
// TerrainToolDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.ClientSize = new System.Drawing.Size(411, 466);
this.ControlBox = false;
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(424, 421);
this.Name = "TerrainToolDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Terrain";
this.tableLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private Controls.TypeComboBox terrainTypeComboBox;
private Controls.MapPanel terrainTypeMapPanel;
private Controls.TerrainProperties terrainProperties;
}
}

View file

@ -0,0 +1,58 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Interface;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MobiusEditor.Tools.Dialogs
{
public partial class TerrainToolDialog : Form
{
public TypeComboBox TerrainTypeComboBox => terrainTypeComboBox;
public MapPanel TerrainTypeMapPanel => terrainTypeMapPanel;
public TerrainProperties TerrainProperties => terrainProperties;
public TerrainToolDialog()
{
InitializeComponent();
Location = Properties.Settings.Default.ToolDialogPosition;
}
public TerrainToolDialog(IGamePlugin plugin)
: this()
{
terrainProperties.Initialize(plugin, true);
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.ToolDialogPosition = Location;
Properties.Settings.Default.Save();
}
}
}

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="terrainTypeComboBox.MissingThumbnail" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
fwDWYGmBKz70PQAAAABJRU5ErkJggg==
</value>
</data>
</root>

View file

@ -0,0 +1,125 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
namespace MobiusEditor.Tools.Dialogs
{
partial class WaypointsToolDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tableLayoutPanel4 = new System.Windows.Forms.TableLayoutPanel();
this.label1 = new System.Windows.Forms.Label();
this.waypointCombo = new System.Windows.Forms.ComboBox();
this.tableLayoutPanel4.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel4
//
this.tableLayoutPanel4.AutoSize = true;
this.tableLayoutPanel4.ColumnCount = 2;
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel4.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel4.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel4.Controls.Add(this.waypointCombo, 1, 0);
this.tableLayoutPanel4.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel4.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel4.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.tableLayoutPanel4.Name = "tableLayoutPanel4";
this.tableLayoutPanel4.RowCount = 2;
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.Size = new System.Drawing.Size(218, 118);
this.tableLayoutPanel4.TabIndex = 3;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
this.label1.Location = new System.Drawing.Point(4, 0);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(75, 38);
this.label1.TabIndex = 0;
this.label1.Text = "Waypoint";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
//
// waypointCombo
//
this.waypointCombo.DisplayMember = "Name";
this.waypointCombo.Dock = System.Windows.Forms.DockStyle.Fill;
this.waypointCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.waypointCombo.FormattingEnabled = true;
this.waypointCombo.Location = new System.Drawing.Point(87, 5);
this.waypointCombo.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.waypointCombo.Name = "waypointCombo";
this.waypointCombo.Size = new System.Drawing.Size(127, 28);
this.waypointCombo.TabIndex = 1;
this.waypointCombo.ValueMember = "Type";
//
// WaypointsToolDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.ClientSize = new System.Drawing.Size(218, 118);
this.ControlBox = false;
this.Controls.Add(this.tableLayoutPanel4);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.MinimumSize = new System.Drawing.Size(210, 91);
this.Name = "WaypointsToolDialog";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "Waypoints";
this.tableLayoutPanel4.ResumeLayout(false);
this.tableLayoutPanel4.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel4;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.ComboBox waypointCombo;
}
}

View file

@ -0,0 +1,39 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using System;
using System.Windows.Forms;
namespace MobiusEditor.Tools.Dialogs
{
public partial class WaypointsToolDialog : Form
{
public ComboBox WaypointCombo => waypointCombo;
public WaypointsToolDialog()
{
InitializeComponent();
Location = Properties.Settings.Default.ToolDialogPosition;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
Properties.Settings.Default.ToolDialogPosition = Location;
Properties.Settings.Default.Save();
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,539 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Render;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class InfantryTool : ViewTool
{
private readonly TypeComboBox infantryTypeComboBox;
private readonly MapPanel infantryTypeMapPanel;
private readonly ObjectProperties objectProperties;
private Map previewMap;
protected override Map RenderMap => previewMap;
private bool placementMode;
private readonly Infantry mockInfantry;
private Infantry selectedInfantry;
private ObjectPropertiesPopup selectedObjectProperties;
private InfantryType selectedInfantryType;
private InfantryType SelectedInfantryType
{
get => selectedInfantryType;
set
{
if (selectedInfantryType != value)
{
if (placementMode && (selectedInfantryType != null))
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
selectedInfantryType = value;
infantryTypeComboBox.SelectedValue = selectedInfantryType;
if (placementMode && (selectedInfantryType != null))
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
mockInfantry.Type = selectedInfantryType;
RefreshMapPanel();
}
}
}
public InfantryTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox infantryTypeComboBox, MapPanel infantryTypeMapPanel, ObjectProperties objectProperties, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
previewMap = map;
mockInfantry = new Infantry(null)
{
Type = infantryTypeComboBox.Types.First() as InfantryType,
House = map.Houses.First().Type,
Strength = 256,
Direction = map.DirectionTypes.Where(d => d.Equals(FacingType.South)).First(),
Mission = map.MissionTypes.Where(m => m.Equals("Guard")).FirstOrDefault() ?? map.MissionTypes.First()
};
mockInfantry.PropertyChanged += MockInfantry_PropertyChanged;
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseUp += MapPanel_MouseUp;
this.mapPanel.MouseDoubleClick += MapPanel_MouseDoubleClick;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += InfantryTool_KeyDown;
(this.mapPanel as Control).KeyUp += InfantryTool_KeyUp;
this.infantryTypeComboBox = infantryTypeComboBox;
this.infantryTypeComboBox.SelectedIndexChanged += InfantryTypeComboBox_SelectedIndexChanged;
this.infantryTypeMapPanel = infantryTypeMapPanel;
this.infantryTypeMapPanel.BackColor = Color.White;
this.infantryTypeMapPanel.MaxZoom = 1;
this.objectProperties = objectProperties;
this.objectProperties.Object = mockInfantry;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
SelectedInfantryType = this.infantryTypeComboBox.Types.First() as InfantryType;
UpdateStatus();
}
private void MapPanel_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (Control.ModifierKeys != Keys.None)
{
return;
}
if (map.Metrics.GetCell(navigationWidget.MouseCell, out int cell))
{
if (map.Technos[cell] is InfantryGroup infantryGroup)
{
var i = InfantryGroup.ClosestStoppingTypes(navigationWidget.MouseSubPixel).Cast<int>().First();
if (infantryGroup.Infantry[i] is Infantry infantry)
{
selectedInfantry = null;
selectedObjectProperties?.Close();
selectedObjectProperties = new ObjectPropertiesPopup(objectProperties.Plugin, infantry);
selectedObjectProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
};
infantry.PropertyChanged += SelectedInfantry_PropertyChanged;
selectedObjectProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
UpdateStatus();
}
}
}
}
private void MockInfantry_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
RefreshMapPanel();
}
private void SelectedInfantry_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
mapPanel.Invalidate(map, (sender as Infantry).InfantryGroup);
}
private void InfantryTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedInfantryType = infantryTypeComboBox.SelectedValue as InfantryType;
}
private void InfantryTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void InfantryTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
if (placementMode)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
else if (selectedInfantry != null)
{
var oldLocation = map.Technos[selectedInfantry.InfantryGroup].Value;
var oldStop = Array.IndexOf(selectedInfantry.InfantryGroup.Infantry, selectedInfantry);
InfantryGroup infantryGroup = null;
var techno = map.Technos[navigationWidget.MouseCell];
if (techno == null)
{
infantryGroup = new InfantryGroup();
map.Technos.Add(navigationWidget.MouseCell, infantryGroup);
}
else if (techno is InfantryGroup)
{
infantryGroup = techno as InfantryGroup;
}
if (infantryGroup != null)
{
foreach (var i in InfantryGroup.ClosestStoppingTypes(navigationWidget.MouseSubPixel).Cast<int>())
{
if (infantryGroup.Infantry[i] == null)
{
selectedInfantry.InfantryGroup.Infantry[oldStop] = null;
infantryGroup.Infantry[i] = selectedInfantry;
if (infantryGroup != selectedInfantry.InfantryGroup)
{
mapPanel.Invalidate(map, selectedInfantry.InfantryGroup);
if (selectedInfantry.InfantryGroup.Infantry.All(x => x == null))
{
map.Technos.Remove(selectedInfantry.InfantryGroup);
}
}
selectedInfantry.InfantryGroup = infantryGroup;
mapPanel.Invalidate(map, infantryGroup);
plugin.Dirty = true;
}
if (infantryGroup == selectedInfantry.InfantryGroup)
{
break;
}
}
}
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
AddInfantry(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveInfantry(navigationWidget.MouseCell);
}
}
else if (e.Button == MouseButtons.Left)
{
SelectInfantry(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
PickInfantry(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if (selectedInfantry != null)
{
selectedInfantry = null;
UpdateStatus();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (SelectedInfantryType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.OldCell, new Size(1, 1)), 1, 1));
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.NewCell, new Size(1, 1)), 1, 1));
}
}
}
private void AddInfantry(Point location)
{
if (SelectedInfantryType != null)
{
if (map.Metrics.GetCell(location, out int cell))
{
InfantryGroup infantryGroup = null;
var techno = map.Technos[cell];
if (techno == null)
{
infantryGroup = new InfantryGroup();
map.Technos.Add(cell, infantryGroup);
}
else if (techno is InfantryGroup)
{
infantryGroup = techno as InfantryGroup;
}
if (infantryGroup != null)
{
foreach (var i in InfantryGroup.ClosestStoppingTypes(navigationWidget.MouseSubPixel).Cast<int>())
{
if (infantryGroup.Infantry[i] == null)
{
var infantry = mockInfantry.Clone();
infantryGroup.Infantry[i] = infantry;
infantry.InfantryGroup = infantryGroup;
mapPanel.Invalidate(map, infantryGroup);
plugin.Dirty = true;
break;
}
}
}
}
}
}
private void RemoveInfantry(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
if (map.Technos[cell] is InfantryGroup infantryGroup)
{
foreach (var i in InfantryGroup.ClosestStoppingTypes(navigationWidget.MouseSubPixel).Cast<int>())
{
if (infantryGroup.Infantry[i] != null)
{
infantryGroup.Infantry[i] = null;
mapPanel.Invalidate(map, infantryGroup);
plugin.Dirty = true;
break;
}
}
if (infantryGroup.Infantry.All(i => i == null))
{
map.Technos.Remove(infantryGroup);
}
}
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedInfantryType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedInfantryType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}
private void PickInfantry(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
if (map.Technos[cell] is InfantryGroup infantryGroup)
{
var i = InfantryGroup.ClosestStoppingTypes(navigationWidget.MouseSubPixel).Cast<int>().First();
if (infantryGroup.Infantry[i] is Infantry infantry)
{
SelectedInfantryType = infantry.Type;
mockInfantry.House = infantry.House;
mockInfantry.Strength = infantry.Strength;
mockInfantry.Direction = infantry.Direction;
mockInfantry.Mission = infantry.Mission;
mockInfantry.Trigger = infantry.Trigger;
}
}
}
}
private void SelectInfantry(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
selectedInfantry = null;
if (map.Technos[cell] is InfantryGroup infantryGroup)
{
var i = InfantryGroup.ClosestStoppingTypes(navigationWidget.MouseSubPixel).Cast<int>().First();
if (infantryGroup.Infantry[i] is Infantry infantry)
{
selectedInfantry = infantry;
}
}
}
UpdateStatus();
}
private void RefreshMapPanel()
{
if (mockInfantry.Type != null)
{
var infantryPreview = new Bitmap(Globals.TileWidth, Globals.TileHeight);
using (var g = Graphics.FromImage(infantryPreview))
{
MapRenderer.Render(map.Theater, Point.Empty, Globals.TileSize, mockInfantry, InfantryStoppingType.Center).Item2(g);
}
infantryTypeMapPanel.MapImage = infantryPreview;
}
else
{
infantryTypeMapPanel.MapImage = null;
}
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to place infantry, Right-Click to remove infantry";
}
else if (selectedInfantry != null)
{
statusLbl.Text = "Drag mouse to move infantry";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click drag to move infantry, Double-Click update infantry properties, Right-Click to pick infantry";
}
}
protected override void PreRenderMap()
{
base.PreRenderMap();
previewMap = map.Clone();
if (placementMode)
{
var location = navigationWidget.MouseCell;
if (SelectedInfantryType != null)
{
if (previewMap.Metrics.GetCell(location, out int cell))
{
InfantryGroup infantryGroup = null;
var techno = previewMap.Technos[cell];
if (techno == null)
{
infantryGroup = new InfantryGroup();
previewMap.Technos.Add(cell, infantryGroup);
}
else if (techno is InfantryGroup)
{
infantryGroup = techno as InfantryGroup;
}
if (infantryGroup != null)
{
foreach (var i in InfantryGroup.ClosestStoppingTypes(navigationWidget.MouseSubPixel).Cast<int>())
{
if (infantryGroup.Infantry[i] == null)
{
var infantry = mockInfantry.Clone();
infantry.Tint = Color.FromArgb(128, Color.White);
infantryGroup.Infantry[i] = infantry;
break;
}
}
}
}
}
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var infantryPen = new Pen(Color.Green, 4.0f);
foreach (var (topLeft, _) in map.Technos.OfType<InfantryGroup>())
{
var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize);
graphics.DrawRectangle(infantryPen, bounds);
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseUp -= MapPanel_MouseUp;
mapPanel.MouseDoubleClick -= MapPanel_MouseDoubleClick;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= InfantryTool_KeyDown;
(mapPanel as Control).KeyUp -= InfantryTool_KeyUp;
infantryTypeComboBox.SelectedIndexChanged -= InfantryTypeComboBox_SelectedIndexChanged;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,341 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class OverlaysTool : ViewTool
{
private readonly TypeComboBox overlayTypeComboBox;
private readonly MapPanel overlayTypeMapPanel;
private Map previewMap;
protected override Map RenderMap => previewMap;
private bool placementMode;
private OverlayType selectedOverlayType;
private OverlayType SelectedOverlayType
{
get => selectedOverlayType;
set
{
if (selectedOverlayType != value)
{
if (placementMode && (selectedOverlayType != null))
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
selectedOverlayType = value;
overlayTypeComboBox.SelectedValue = selectedOverlayType;
RefreshMapPanel();
}
}
}
public OverlaysTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox overlayTypeComboBox, MapPanel overlayTypeMapPanel, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
previewMap = map;
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += OverlaysTool_KeyDown;
(this.mapPanel as Control).KeyUp += OverlaysTool_KeyUp;
this.overlayTypeComboBox = overlayTypeComboBox;
this.overlayTypeComboBox.SelectedIndexChanged += OverlayTypeComboBox_SelectedIndexChanged;
this.overlayTypeMapPanel = overlayTypeMapPanel;
this.overlayTypeMapPanel.BackColor = Color.White;
this.overlayTypeMapPanel.MaxZoom = 1;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
SelectedOverlayType = this.overlayTypeComboBox.Types.First() as OverlayType;
UpdateStatus();
}
private void OverlayTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedOverlayType = overlayTypeComboBox.SelectedValue as OverlayType;
}
private void OverlaysTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void OverlaysTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
AddOverlay(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveOverlay(navigationWidget.MouseCell);
}
}
else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right))
{
PickOverlay(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (SelectedOverlayType != null)
{
mapPanel.Invalidate(map, new Rectangle(e.OldCell, new Size(1, 1)));
mapPanel.Invalidate(map, new Rectangle(e.NewCell, new Size(1, 1)));
}
}
}
private void AddOverlay(Point location)
{
if ((location.Y == 0) || (location.Y == (map.Metrics.Height - 1)))
{
return;
}
if (map.Overlay[location] == null)
{
if (SelectedOverlayType != null)
{
var overlay = new Overlay
{
Type = SelectedOverlayType,
Icon = 0
};
map.Overlay[location] = overlay;
mapPanel.Invalidate(map, location);
void undoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, location);
e.Map.Overlay[location] = null;
}
void redoAction(UndoRedoEventArgs e)
{
e.Map.Overlay[location] = overlay;
e.MapPanel.Invalidate(e.Map, location);
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
}
private void RemoveOverlay(Point location)
{
if ((map.Overlay[location] is Overlay overlay) && overlay.Type.IsPlaceable)
{
map.Overlay[location] = null;
mapPanel.Invalidate(map, location);
void undoAction(UndoRedoEventArgs e)
{
e.Map.Overlay[location] = overlay;
e.MapPanel.Invalidate(e.Map, location);
}
void redoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, location);
e.Map.Overlay[location] = null;
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedOverlayType != null)
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedOverlayType != null)
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
UpdateStatus();
}
private void PickOverlay(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var overlay = map.Overlay[cell];
if ((overlay != null) && !overlay.Type.IsWall)
{
SelectedOverlayType = overlay.Type;
}
}
}
private void RefreshMapPanel()
{
overlayTypeMapPanel.MapImage = SelectedOverlayType?.Thumbnail;
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to place overlay, Right-Click to remove overlay";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick overlay";
}
}
protected override void PreRenderMap()
{
base.PreRenderMap();
previewMap = map.Clone();
if (placementMode)
{
var location = navigationWidget.MouseCell;
if (SelectedOverlayType != null)
{
if (previewMap.Metrics.GetCell(location, out int cell))
{
if (previewMap.Overlay[cell] == null)
{
previewMap.Overlay[cell] = new Overlay
{
Type = SelectedOverlayType,
Icon = 0,
Tint = Color.FromArgb(128, Color.White)
};
}
}
}
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var overlayPen = new Pen(Color.Green, 4.0f);
foreach (var (cell, overlay) in previewMap.Overlay.Where(x => x.Value.Type.IsPlaceable))
{
previewMap.Metrics.GetLocation(cell, out Point topLeft);
var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize);
graphics.DrawRectangle(overlayPen, bounds);
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
overlayTypeComboBox.SelectedIndexChanged -= OverlayTypeComboBox_SelectedIndexChanged;
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= OverlaysTool_KeyDown;
(mapPanel as Control).KeyUp -= OverlaysTool_KeyUp;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,364 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class ResourcesTool : ViewTool
{
private readonly Label totalResourcesLbl;
private readonly NumericUpDown brushSizeNud;
private readonly CheckBox gemsCheckBox;
private bool placementMode;
private bool additivePlacement;
private readonly Dictionary<int, Overlay> undoOverlays = new Dictionary<int, Overlay>();
private readonly Dictionary<int, Overlay> redoOverlays = new Dictionary<int, Overlay>();
public ResourcesTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, Label totalResourcesLbl, NumericUpDown brushSizeNud, CheckBox gemsCheckBox, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseUp += MapPanel_MouseUp;
(this.mapPanel as Control).KeyDown += ResourceTool_KeyDown;
this.totalResourcesLbl = totalResourcesLbl;
this.brushSizeNud = brushSizeNud;
this.gemsCheckBox = gemsCheckBox;
this.brushSizeNud.ValueChanged += BrushSizeNud_ValueChanged;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
navigationWidget.MouseoverSize = new Size((int)brushSizeNud.Value, (int)brushSizeNud.Value);
url.Undone += Url_UndoRedo;
url.Redone += Url_UndoRedo;
Update();
UpdateStatus();
}
private void Url_UndoRedo(object sender, EventArgs e)
{
Update();
}
private void BrushSizeNud_ValueChanged(object sender, EventArgs e)
{
navigationWidget.MouseoverSize = new Size((int)brushSizeNud.Value, (int)brushSizeNud.Value);
}
private void ResourceTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.OemOpenBrackets)
{
brushSizeNud.DownButton();
mapPanel.Invalidate();
}
else if (e.KeyCode == Keys.OemCloseBrackets)
{
brushSizeNud.UpButton();
mapPanel.Invalidate();
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (!placementMode)
{
EnterPlacementMode(true);
AddResource(navigationWidget.MouseCell);
}
}
else if (e.Button == MouseButtons.Right)
{
if (!placementMode)
{
EnterPlacementMode(false);
RemoveResource(navigationWidget.MouseCell);
}
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (((e.Button == MouseButtons.Left) && additivePlacement) ||
((e.Button == MouseButtons.Right) && !additivePlacement))
{
ExitPlacementMode();
}
}
if ((undoOverlays.Count > 0) || (redoOverlays.Count > 0))
{
CommitChange();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (additivePlacement)
{
AddResource(e.NewCell);
}
else
{
RemoveResource(e.NewCell);
}
}
if (brushSizeNud.Value > 1)
{
foreach (var cell in new Point[] { e.OldCell, e.NewCell })
{
mapPanel.Invalidate(mapPanel.MapToClient(new Rectangle(
new Point(cell.X - ((int)brushSizeNud.Value / 2), cell.Y - ((int)brushSizeNud.Value / 2)),
new Size((int)brushSizeNud.Value, (int)brushSizeNud.Value)
)));
}
}
}
private void AddResource(Point location)
{
Rectangle rectangle = new Rectangle(location, new Size(1, 1));
rectangle.Inflate(navigationWidget.MouseoverSize.Width / 2, navigationWidget.MouseoverSize.Height / 2);
foreach (var subLocation in rectangle.Points())
{
if ((subLocation.Y == 0) || (subLocation.Y == (map.Metrics.Height - 1)))
{
continue;
}
if (map.Metrics.GetCell(subLocation, out int cell))
{
if (map.Overlay[cell] == null)
{
var resourceType = gemsCheckBox.Checked ?
map.OverlayTypes.Where(t => t.IsGem).FirstOrDefault() :
map.OverlayTypes.Where(t => t.IsTiberiumOrGold).FirstOrDefault();
if (resourceType != null)
{
if (!undoOverlays.ContainsKey(cell))
{
undoOverlays[cell] = map.Overlay[cell];
}
var overlay = new Overlay { Type = resourceType, Icon = 0 };
map.Overlay[cell] = overlay;
redoOverlays[cell] = overlay;
plugin.Dirty = true;
}
}
}
}
rectangle.Inflate(1, 1);
mapPanel.Invalidate(map, rectangle);
Update();
}
private void RemoveResource(Point location)
{
Rectangle rectangle = new Rectangle(location, new Size(1, 1));
rectangle.Inflate(navigationWidget.MouseoverSize.Width / 2, navigationWidget.MouseoverSize.Height / 2);
foreach (var subLocation in rectangle.Points())
{
if (map.Metrics.GetCell(subLocation, out int cell))
{
if (map.Overlay[cell]?.Type.IsResource ?? false)
{
if (!undoOverlays.ContainsKey(cell))
{
undoOverlays[cell] = map.Overlay[cell];
}
map.Overlay[cell] = null;
redoOverlays[cell] = null;
plugin.Dirty = true;
}
}
}
rectangle.Inflate(1, 1);
mapPanel.Invalidate(map, rectangle);
Update();
}
private void EnterPlacementMode(bool additive)
{
if (placementMode)
{
return;
}
placementMode = true;
additivePlacement = additive;
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
UpdateStatus();
}
private void CommitChange()
{
var undoOverlays2 = new Dictionary<int, Overlay>(undoOverlays);
void undoAction(UndoRedoEventArgs e)
{
foreach (var kv in undoOverlays2)
{
e.Map.Overlay[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate(e.Map, undoOverlays2.Keys.Select(k =>
{
e.Map.Metrics.GetLocation(k, out Point location);
var rectangle = new Rectangle(location, new Size(1, 1));
rectangle.Inflate(1, 1);
return rectangle;
}));
}
var redoOverlays2 = new Dictionary<int, Overlay>(redoOverlays);
void redoAction(UndoRedoEventArgs e)
{
foreach (var kv in redoOverlays2)
{
e.Map.Overlay[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate(e.Map, redoOverlays2.Keys.Select(k =>
{
e.Map.Metrics.GetLocation(k, out Point location);
var rectangle = new Rectangle(location, new Size(1, 1));
rectangle.Inflate(1, 1);
return rectangle;
}));
}
undoOverlays.Clear();
redoOverlays.Clear();
url.Track(undoAction, redoAction);
}
private void Update()
{
totalResourcesLbl.Text = map.TotalResources.ToString();
if (map.OverlayTypes.Any(t => t.IsGem))
{
gemsCheckBox.Visible = true;
}
else
{
gemsCheckBox.Visible = false;
gemsCheckBox.Checked = false;
}
}
private void UpdateStatus()
{
if (placementMode)
{
if (additivePlacement)
{
statusLbl.Text = "Drag mouse to add resources";
}
else
{
statusLbl.Text = "Drag mouse to remove resources";
}
}
else
{
statusLbl.Text = "Left-Click drag to add resources, Right-Click drag to remove resources";
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var resourcePen = new Pen(Color.Green, 4.0f);
foreach (var (cell, overlay) in map.Overlay)
{
if (overlay.Type.IsResource)
{
map.Metrics.GetLocation(cell, out Point topLeft);
var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize);
graphics.DrawRectangle(resourcePen, bounds);
}
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseUp -= MapPanel_MouseUp;
(mapPanel as Control).KeyDown -= ResourceTool_KeyDown;
brushSizeNud.ValueChanged -= BrushSizeNud_ValueChanged;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
url.Undone -= Url_UndoRedo;
url.Redone -= Url_UndoRedo;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,337 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class SmudgeTool : ViewTool
{
private readonly TypeComboBox smudgeTypeComboBox;
private readonly MapPanel smudgeTypeMapPanel;
private Map previewMap;
protected override Map RenderMap => previewMap;
private bool placementMode;
private SmudgeType selectedSmudgeType;
private SmudgeType SelectedSmudgeType
{
get => selectedSmudgeType;
set
{
if (selectedSmudgeType != value)
{
if (placementMode && (selectedSmudgeType != null))
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
selectedSmudgeType = value;
smudgeTypeComboBox.SelectedValue = selectedSmudgeType;
if (placementMode && (selectedSmudgeType != null))
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
RefreshMapPanel();
}
}
}
public SmudgeTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox smudgeTypeComboBox, MapPanel smudgeTypeMapPanel, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
previewMap = map;
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += SmudgeTool_KeyDown;
(this.mapPanel as Control).KeyUp += SmudgeTool_KeyUp;
this.smudgeTypeComboBox = smudgeTypeComboBox;
this.smudgeTypeComboBox.SelectedIndexChanged += SmudgeTypeComboBox_SelectedIndexChanged;
this.smudgeTypeMapPanel = smudgeTypeMapPanel;
this.smudgeTypeMapPanel.BackColor = Color.White;
this.smudgeTypeMapPanel.MaxZoom = 1;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
SelectedSmudgeType = smudgeTypeComboBox.Types.First() as SmudgeType;
UpdateStatus();
}
private void SmudgeTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedSmudgeType = smudgeTypeComboBox.SelectedValue as SmudgeType;
}
private void SmudgeTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void SmudgeTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
AddSmudge(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveSmudge(navigationWidget.MouseCell);
}
}
else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right))
{
PickSmudge(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (SelectedSmudgeType != null)
{
mapPanel.Invalidate(map, e.OldCell);
mapPanel.Invalidate(map, e.NewCell);
}
}
}
private void AddSmudge(Point location)
{
if (map.Smudge[location] == null)
{
if (SelectedSmudgeType != null)
{
var smudge = new Smudge
{
Type = SelectedSmudgeType,
Icon = 0,
Data = 0
};
map.Smudge[location] = smudge;
mapPanel.Invalidate(map, location);
void undoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, location);
e.Map.Smudge[location] = null;
}
void redoAction(UndoRedoEventArgs e)
{
e.Map.Smudge[location] = smudge;
e.MapPanel.Invalidate(e.Map, location);
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
}
private void RemoveSmudge(Point location)
{
if ((map.Smudge[location] is Smudge smudge) && ((smudge.Type.Flag & SmudgeTypeFlag.Bib) == SmudgeTypeFlag.None))
{
map.Smudge[location] = null;
mapPanel.Invalidate(map, location);
void undoAction(UndoRedoEventArgs e)
{
e.Map.Smudge[location] = smudge;
e.MapPanel.Invalidate(e.Map, location);
}
void redoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, location);
e.Map.Smudge[location] = null;
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedSmudgeType != null)
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedSmudgeType != null)
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
UpdateStatus();
}
private void PickSmudge(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var smudge = map.Smudge[cell];
if (smudge != null)
{
SelectedSmudgeType = smudge.Type;
}
}
}
private void RefreshMapPanel()
{
smudgeTypeMapPanel.MapImage = SelectedSmudgeType?.Thumbnail;
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to place smudge, Right-Click to remove smudge";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick smudge";
}
}
protected override void PreRenderMap()
{
base.PreRenderMap();
previewMap = map.Clone();
if (placementMode)
{
var location = navigationWidget.MouseCell;
if (SelectedSmudgeType != null)
{
if (previewMap.Metrics.GetCell(location, out int cell))
{
if (previewMap.Smudge[cell] == null)
{
previewMap.Smudge[cell] = new Smudge { Type = SelectedSmudgeType, Data = 0, Tint = Color.FromArgb(128, Color.White) };
}
}
}
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var smudgePen = new Pen(Color.Green, 4.0f);
foreach (var (cell, smudge) in previewMap.Smudge.Where(x => (x.Value.Type.Flag & SmudgeTypeFlag.Bib) == SmudgeTypeFlag.None))
{
previewMap.Metrics.GetLocation(cell, out Point topLeft);
var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize);
graphics.DrawRectangle(smudgePen, bounds);
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
smudgeTypeComboBox.SelectedIndexChanged -= SmudgeTypeComboBox_SelectedIndexChanged;
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= SmudgeTool_KeyDown;
(mapPanel as Control).KeyUp -= SmudgeTool_KeyUp;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,455 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class TerrainTool : ViewTool
{
private readonly TypeComboBox terrainTypeComboBox;
private readonly MapPanel terrainTypeMapPanel;
private readonly TerrainProperties terrainProperties;
private Map previewMap;
protected override Map RenderMap => previewMap;
private bool placementMode;
private readonly Terrain mockTerrain;
private Terrain selectedTerrain;
private Point selectedTerrainPivot;
private TerrainType selectedTerrainType;
private TerrainPropertiesPopup selectedTerrainProperties;
private TerrainType SelectedTerrainType
{
get => selectedTerrainType;
set
{
if (selectedTerrainType != value)
{
if (placementMode && (selectedTerrainType != null))
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, selectedTerrainType.OverlapBounds.Size));
}
selectedTerrainType = value;
terrainTypeComboBox.SelectedValue = selectedTerrainType;
if (placementMode && (selectedTerrainType != null))
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, selectedTerrainType.OverlapBounds.Size));
}
mockTerrain.Type = selectedTerrainType;
mockTerrain.Icon = selectedTerrainType.IsTransformable ? 22 : 0;
RefreshMapPanel();
}
}
}
public TerrainTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox terrainTypeComboBox, MapPanel terrainTypeMapPanel, TerrainProperties terrainProperties, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
previewMap = map;
mockTerrain = new Terrain();
mockTerrain.PropertyChanged += MockTerrain_PropertyChanged;
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseMove += MapPanel_MouseMove;
this.mapPanel.MouseUp += MapPanel_MouseUp;
this.mapPanel.MouseDoubleClick += MapPanel_MouseDoubleClick;
(this.mapPanel as Control).KeyDown += TerrainTool_KeyDown;
(this.mapPanel as Control).KeyUp += TerrainTool_KeyUp;
this.terrainTypeComboBox = terrainTypeComboBox;
this.terrainTypeComboBox.SelectedIndexChanged += TerrainTypeCombo_SelectedIndexChanged;
this.terrainTypeMapPanel = terrainTypeMapPanel;
this.terrainTypeMapPanel.BackColor = Color.White;
this.terrainTypeMapPanel.MaxZoom = 1;
this.terrainProperties = terrainProperties;
this.terrainProperties.Terrain = mockTerrain;
this.terrainProperties.Visible = plugin.GameType == GameType.TiberianDawn;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
SelectedTerrainType = terrainTypeComboBox.Types.First() as TerrainType;
UpdateStatus();
}
private void MapPanel_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (Control.ModifierKeys != Keys.None)
{
return;
}
if (map.Metrics.GetCell(navigationWidget.MouseCell, out int cell))
{
if (map.Technos[cell] is Terrain terrain)
{
selectedTerrain = null;
selectedTerrainProperties?.Close();
selectedTerrainProperties = new TerrainPropertiesPopup(terrainProperties.Plugin, terrain);
selectedTerrainProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
};
selectedTerrainProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
UpdateStatus();
}
}
}
private void MockTerrain_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
RefreshMapPanel();
}
private void TerrainTypeCombo_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedTerrainType = terrainTypeComboBox.SelectedValue as TerrainType;
}
private void TerrainTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void TerrainTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
AddTerrain(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveTerrain(navigationWidget.MouseCell);
}
}
else if (e.Button == MouseButtons.Left)
{
SelectTerrain(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
PickTerrain(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if (selectedTerrain != null)
{
selectedTerrain = null;
selectedTerrainPivot = Point.Empty;
UpdateStatus();
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (SelectedTerrainType != null)
{
mapPanel.Invalidate(map, new Rectangle(e.OldCell, SelectedTerrainType.OverlapBounds.Size));
mapPanel.Invalidate(map, new Rectangle(e.NewCell, SelectedTerrainType.OverlapBounds.Size));
}
}
else if (selectedTerrain != null)
{
var oldLocation = map.Technos[selectedTerrain].Value;
var newLocation = new Point(Math.Max(0, e.NewCell.X - selectedTerrainPivot.X), Math.Max(0, e.NewCell.Y - selectedTerrainPivot.Y));
mapPanel.Invalidate(map, selectedTerrain);
map.Technos.Remove(selectedTerrain);
if (map.Technos.Add(newLocation, selectedTerrain))
{
mapPanel.Invalidate(map, selectedTerrain);
}
else
{
map.Technos.Add(oldLocation, selectedTerrain);
}
}
}
private void AddTerrain(Point location)
{
if (!map.Metrics.Contains(location))
{
return;
}
if (SelectedTerrainType != null)
{
var terrain = mockTerrain.Clone();
if (map.Technos.Add(location, terrain))
{
mapPanel.Invalidate(map, terrain);
void undoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, location);
e.Map.Technos.Remove(terrain);
}
void redoAction(UndoRedoEventArgs e)
{
e.Map.Technos.Add(location, terrain);
e.MapPanel.Invalidate(e.Map, location);
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
}
private void RemoveTerrain(Point location)
{
if (map.Technos[location] is Terrain terrain)
{
mapPanel.Invalidate(map, terrain);
map.Technos.Remove(location);
void undoAction(UndoRedoEventArgs e)
{
e.Map.Technos.Add(location, terrain);
e.MapPanel.Invalidate(e.Map, location);
}
void redoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, location);
e.Map.Technos.Remove(terrain);
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedTerrainType != null)
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, selectedTerrainType.OverlapBounds.Size));
}
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedTerrainType != null)
{
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, selectedTerrainType.OverlapBounds.Size));
}
UpdateStatus();
}
private void PickTerrain(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
if (map.Technos[cell] is Terrain terrain)
{
SelectedTerrainType = terrain.Type;
mockTerrain.Trigger = terrain.Trigger;
}
}
}
private void SelectTerrain(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
selectedTerrain = map.Technos[cell] as Terrain;
selectedTerrainPivot = (selectedTerrain != null) ? (location - (Size)map.Technos[selectedTerrain].Value) : Point.Empty;
}
UpdateStatus();
}
private void RefreshMapPanel()
{
terrainTypeMapPanel.MapImage = mockTerrain.Type.Thumbnail;
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to place terrain, Right-Click to remove terrain";
}
else if (selectedTerrain != null)
{
statusLbl.Text = "Drag mouse to move terrain";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click drag to move terrain, Double-Click update terrain properties, Right-Click to pick terrain";
}
}
protected override void PreRenderMap()
{
base.PreRenderMap();
previewMap = map.Clone();
if (placementMode)
{
var location = navigationWidget.MouseCell;
if (SelectedTerrainType != null)
{
if (previewMap.Metrics.Contains(location))
{
var terrain = new Terrain
{
Type = SelectedTerrainType,
Icon = SelectedTerrainType.IsTransformable ? 22 : 0,
Tint = Color.FromArgb(128, Color.White)
};
previewMap.Technos.Add(location, terrain);
}
}
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var terrainPen = new Pen(Color.Green, 4.0f);
var occupyPen = new Pen(Color.Red, 2.0f);
foreach (var (topLeft, terrain) in previewMap.Technos.OfType<Terrain>())
{
var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), terrain.Type.RenderSize);
graphics.DrawRectangle(terrainPen, bounds);
for (var y = 0; y < terrain.Type.OccupyMask.GetLength(0); ++y)
{
for (var x = 0; x < terrain.Type.OccupyMask.GetLength(1); ++x)
{
if (terrain.Type.OccupyMask[y, x])
{
var occupyBounds = new Rectangle(
new Point((topLeft.X + x) * Globals.TileWidth, (topLeft.Y + y) * Globals.TileHeight),
Globals.TileSize
);
graphics.DrawRectangle(occupyPen, occupyBounds);
}
}
}
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
selectedTerrainProperties?.Close();
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseMove -= MapPanel_MouseMove;
mapPanel.MouseUp -= MapPanel_MouseUp;
mapPanel.MouseDoubleClick -= MapPanel_MouseDoubleClick;
(mapPanel as Control).KeyDown -= TerrainTool_KeyDown;
(mapPanel as Control).KeyUp -= TerrainTool_KeyUp;
terrainTypeComboBox.SelectedIndexChanged -= TerrainTypeCombo_SelectedIndexChanged;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,426 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Render;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class UnitTool : ViewTool
{
private readonly TypeComboBox unitTypeComboBox;
private readonly MapPanel unitTypeMapPanel;
private readonly ObjectProperties objectProperties;
private Map previewMap;
protected override Map RenderMap => previewMap;
private bool placementMode;
private readonly Unit mockUnit;
private Unit selectedUnit;
private ObjectPropertiesPopup selectedObjectProperties;
private UnitType selectedUnitType;
private UnitType SelectedUnitType
{
get => selectedUnitType;
set
{
if (selectedUnitType != value)
{
if (placementMode && (selectedUnitType != null))
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
selectedUnitType = value;
unitTypeComboBox.SelectedValue = selectedUnitType;
if (placementMode && (selectedUnitType != null))
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
mockUnit.Type = selectedUnitType;
RefreshMapPanel();
}
}
}
public UnitTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox unitTypeComboBox, MapPanel unitTypeMapPanel, ObjectProperties objectProperties, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
previewMap = map;
mockUnit = new Unit()
{
Type = unitTypeComboBox.Types.First() as UnitType,
House = map.Houses.First().Type,
Strength = 256,
Direction = map.DirectionTypes.Where(d => d.Equals(FacingType.North)).First(),
Mission = map.MissionTypes.Where(m => m.Equals("Guard")).FirstOrDefault() ?? map.MissionTypes.First()
};
mockUnit.PropertyChanged += MockUnit_PropertyChanged;
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseUp += MapPanel_MouseUp;
this.mapPanel.MouseDoubleClick += MapPanel_MouseDoubleClick;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += UnitTool_KeyDown;
(this.mapPanel as Control).KeyUp += UnitTool_KeyUp;
this.unitTypeComboBox = unitTypeComboBox;
this.unitTypeComboBox.SelectedIndexChanged += UnitTypeComboBox_SelectedIndexChanged;
this.unitTypeMapPanel = unitTypeMapPanel;
this.unitTypeMapPanel.BackColor = Color.White;
this.unitTypeMapPanel.MaxZoom = 1;
this.objectProperties = objectProperties;
this.objectProperties.Object = mockUnit;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
SelectedUnitType = mockUnit.Type;
UpdateStatus();
}
private void MapPanel_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (Control.ModifierKeys != Keys.None)
{
return;
}
if (map.Metrics.GetCell(navigationWidget.MouseCell, out int cell))
{
if (map.Technos[cell] is Unit unit)
{
selectedUnit = null;
selectedObjectProperties?.Close();
selectedObjectProperties = new ObjectPropertiesPopup(objectProperties.Plugin, unit);
selectedObjectProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
};
unit.PropertyChanged += SelectedUnit_PropertyChanged;
selectedObjectProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
UpdateStatus();
}
}
}
private void MockUnit_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
RefreshMapPanel();
}
private void SelectedUnit_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
mapPanel.Invalidate(map, sender as Unit);
}
private void UnitTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedUnitType = unitTypeComboBox.SelectedValue as UnitType;
}
private void UnitTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void UnitTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
AddUnit(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveUnit(navigationWidget.MouseCell);
}
}
else if (e.Button == MouseButtons.Left)
{
SelectUnit(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
PickUnit(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if (selectedUnit != null)
{
selectedUnit = null;
UpdateStatus();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (SelectedUnitType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.OldCell, new Size(1, 1)), 1, 1));
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.NewCell, new Size(1, 1)), 1, 1));
}
}
else if (selectedUnit != null)
{
var oldLocation = map.Technos[selectedUnit].Value;
mapPanel.Invalidate(map, selectedUnit);
map.Technos.Remove(selectedUnit);
if (map.Technos.Add(e.NewCell, selectedUnit))
{
mapPanel.Invalidate(map, selectedUnit);
plugin.Dirty = true;
}
else
{
map.Technos.Add(oldLocation, selectedUnit);
}
}
}
private void AddUnit(Point location)
{
if (SelectedUnitType != null)
{
var unit = mockUnit.Clone();
if (map.Technos.Add(location, unit))
{
mapPanel.Invalidate(map, unit);
plugin.Dirty = true;
}
}
}
private void RemoveUnit(Point location)
{
if (map.Technos[location] is Unit unit)
{
mapPanel.Invalidate(map, unit);
map.Technos.Remove(unit);
plugin.Dirty = true;
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedUnitType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedUnitType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}
private void PickUnit(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
if (map.Technos[cell] is Unit unit)
{
SelectedUnitType = unit.Type;
mockUnit.House = unit.House;
mockUnit.Strength = unit.Strength;
mockUnit.Direction = unit.Direction;
mockUnit.Mission = unit.Mission;
mockUnit.Trigger = unit.Trigger;
}
}
}
private void SelectUnit(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
selectedUnit = map.Technos[cell] as Unit;
}
UpdateStatus();
}
private void RefreshMapPanel()
{
if (mockUnit.Type != null)
{
var unitPreview = new Bitmap(Globals.TileWidth * 3, Globals.TileHeight * 3);
using (var g = Graphics.FromImage(unitPreview))
{
MapRenderer.Render(plugin.GameType, map.Theater, new Point(1, 1), Globals.TileSize, mockUnit).Item2(g);
}
unitTypeMapPanel.MapImage = unitPreview;
}
else
{
unitTypeMapPanel.MapImage = null;
}
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to place unit, Right-Click to remove unit";
}
else if (selectedUnit != null)
{
statusLbl.Text = "Drag mouse to move unit";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click drag to move unit, Double-Click update unit properties, Right-Click to pick unit";
}
}
protected override void PreRenderMap()
{
base.PreRenderMap();
previewMap = map.Clone();
if (placementMode)
{
var location = navigationWidget.MouseCell;
if (SelectedUnitType != null)
{
var unit = mockUnit.Clone();
unit.Tint = Color.FromArgb(128, Color.White);
if (previewMap.Technos.Add(location, unit))
{
mapPanel.Invalidate(previewMap, unit);
}
}
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var unitPen = new Pen(Color.Green, 4.0f);
foreach (var (topLeft, _) in map.Technos.OfType<Unit>())
{
var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize);
graphics.DrawRectangle(unitPen, bounds);
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
selectedObjectProperties?.Close();
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseUp -= MapPanel_MouseUp;
mapPanel.MouseDoubleClick -= MapPanel_MouseDoubleClick;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= UnitTool_KeyDown;
(mapPanel as Control).KeyUp -= UnitTool_KeyUp;
unitTypeComboBox.SelectedIndexChanged -= UnitTypeComboBox_SelectedIndexChanged;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,315 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Render;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public abstract class ViewTool : ITool
{
protected readonly IGamePlugin plugin;
protected readonly Map map;
protected readonly MapPanel mapPanel;
protected readonly ToolStripStatusLabel statusLbl;
protected readonly UndoRedoList<UndoRedoEventArgs> url;
protected readonly NavigationWidget navigationWidget;
protected virtual Map RenderMap => map;
private MapLayerFlag layers;
public MapLayerFlag Layers
{
get => layers;
set
{
if (layers != value)
{
layers = value;
Invalidate();
}
}
}
public ViewTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
{
this.layers = layers;
this.plugin = plugin;
this.url = url;
this.mapPanel = mapPanel;
this.mapPanel.PreRender += MapPanel_PreRender;
this.mapPanel.PostRender += MapPanel_PostRender;
this.statusLbl = statusLbl;
map = plugin.Map;
map.BasicSection.PropertyChanged += BasicSection_PropertyChanged;
navigationWidget = new NavigationWidget(mapPanel, map.Metrics, Globals.TileSize);
}
protected void Invalidate()
{
mapPanel.Invalidate(RenderMap);
}
private void BasicSection_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "BasePlayer":
{
foreach (var baseBuilding in map.Buildings.OfType<Building>().Select(x => x.Occupier).Where(x => x.BasePriority >= 0))
{
mapPanel.Invalidate(map, baseBuilding);
}
}
break;
}
}
private void MapPanel_PreRender(object sender, RenderEventArgs e)
{
if ((e.Cells != null) && (e.Cells.Count == 0))
{
return;
}
PreRenderMap();
using (var g = Graphics.FromImage(mapPanel.MapImage))
{
if (Properties.Settings.Default.Quality > 1)
{
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
}
MapRenderer.Render(plugin.GameType, RenderMap, g, e.Cells?.Where(p => map.Metrics.Contains(p)).ToHashSet(), Layers);
}
}
private void MapPanel_PostRender(object sender, RenderEventArgs e)
{
PostRenderMap(e.Graphics);
navigationWidget.Render(e.Graphics);
}
protected virtual void PreRenderMap() { }
protected virtual void PostRenderMap(Graphics graphics)
{
if ((Layers & MapLayerFlag.Waypoints) != MapLayerFlag.None)
{
var waypointBackgroundBrush = new SolidBrush(Color.FromArgb(96, Color.Black));
var waypointBrush = new SolidBrush(Color.FromArgb(128, Color.DarkOrange));
var waypointPen = new Pen(Color.DarkOrange);
foreach (var waypoint in map.Waypoints)
{
if (waypoint.Cell.HasValue)
{
var x = waypoint.Cell.Value % map.Metrics.Width;
var y = waypoint.Cell.Value / map.Metrics.Width;
var location = new Point(x * Globals.TileWidth, y * Globals.TileHeight);
var textBounds = new Rectangle(location, Globals.TileSize);
graphics.FillRectangle(waypointBackgroundBrush, textBounds);
graphics.DrawRectangle(waypointPen, textBounds);
StringFormat stringFormat = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
var text = waypoint.Name.ToString();
var font = graphics.GetAdjustedFont(text, SystemFonts.DefaultFont, textBounds.Width, 24 / Globals.TileScale, 48 / Globals.TileScale, true);
graphics.DrawString(text.ToString(), font, waypointBrush, textBounds, stringFormat);
}
}
}
if ((Layers & MapLayerFlag.TechnoTriggers) != MapLayerFlag.None)
{
var technoTriggerBackgroundBrush = new SolidBrush(Color.FromArgb(96, Color.Black));
var technoTriggerBrush = new SolidBrush(Color.LimeGreen);
var technoTriggerPen = new Pen(Color.LimeGreen);
foreach (var (cell, techno) in map.Technos)
{
var location = new Point(cell.X * Globals.TileWidth, cell.Y * Globals.TileHeight);
(string trigger, Rectangle bounds)[] triggers = null;
if (techno is Terrain terrain)
{
triggers = new (string, Rectangle)[] { (terrain.Trigger, new Rectangle(location, terrain.Type.RenderSize)) };
}
else if (techno is Building building)
{
var size = new Size(building.Type.Size.Width * Globals.TileWidth, building.Type.Size.Height * Globals.TileHeight);
triggers = new (string, Rectangle)[] { (building.Trigger, new Rectangle(location, size)) };
}
else if (techno is Unit unit)
{
triggers = new (string, Rectangle)[] { (unit.Trigger, new Rectangle(location, Globals.TileSize)) };
}
else if (techno is InfantryGroup infantryGroup)
{
List<(string, Rectangle)> infantryTriggers = new List<(string, Rectangle)>();
for (var i = 0; i < infantryGroup.Infantry.Length; ++i)
{
var infantry = infantryGroup.Infantry[i];
if (infantry == null)
{
continue;
}
var size = Globals.TileSize;
var offset = Size.Empty;
switch ((InfantryStoppingType)i)
{
case InfantryStoppingType.UpperLeft:
offset.Width = -size.Width / 4;
offset.Height = -size.Height / 4;
break;
case InfantryStoppingType.UpperRight:
offset.Width = size.Width / 4;
offset.Height = -size.Height / 4;
break;
case InfantryStoppingType.LowerLeft:
offset.Width = -size.Width / 4;
offset.Height = size.Height / 4;
break;
case InfantryStoppingType.LowerRight:
offset.Width = size.Width / 4;
offset.Height = size.Height / 4;
break;
}
var bounds = new Rectangle(location + offset, size);
infantryTriggers.Add((infantry.Trigger, bounds));
}
triggers = infantryTriggers.ToArray();
}
if (triggers != null)
{
StringFormat stringFormat = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
foreach (var (trigger, bounds) in triggers.Where(x => !x.trigger.Equals("None", StringComparison.OrdinalIgnoreCase)))
{
var font = graphics.GetAdjustedFont(trigger, SystemFonts.DefaultFont, bounds.Width, 12 / Globals.TileScale, 24 / Globals.TileScale, true);
var textBounds = graphics.MeasureString(trigger, font, bounds.Width, stringFormat);
var backgroundBounds = new RectangleF(bounds.Location, textBounds);
backgroundBounds.Offset((bounds.Width - textBounds.Width) / 2.0f, (bounds.Height - textBounds.Height) / 2.0f);
graphics.FillRectangle(technoTriggerBackgroundBrush, backgroundBounds);
graphics.DrawRectangle(technoTriggerPen, Rectangle.Round(backgroundBounds));
graphics.DrawString(trigger, font, technoTriggerBrush, bounds, stringFormat);
}
}
}
}
if ((Layers & MapLayerFlag.CellTriggers) != MapLayerFlag.None)
{
var cellTriggersBackgroundBrush = new SolidBrush(Color.FromArgb(96, Color.Black));
var cellTriggersBrush = new SolidBrush(Color.FromArgb(128, Color.White));
var cellTriggerPen = new Pen(Color.White);
foreach (var (cell, cellTrigger) in map.CellTriggers)
{
var x = cell % map.Metrics.Width;
var y = cell / map.Metrics.Width;
var location = new Point(x * Globals.TileWidth, y * Globals.TileHeight);
var textBounds = new Rectangle(location, Globals.TileSize);
graphics.FillRectangle(cellTriggersBackgroundBrush, textBounds);
graphics.DrawRectangle(cellTriggerPen, textBounds);
StringFormat stringFormat = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
var text = cellTrigger.Trigger;
var font = graphics.GetAdjustedFont(text, SystemFonts.DefaultFont, textBounds.Width, 24 / Globals.TileScale, 48 / Globals.TileScale, true);
graphics.DrawString(text.ToString(), font, cellTriggersBrush, textBounds, stringFormat);
}
}
if ((Layers & MapLayerFlag.Boundaries) != MapLayerFlag.None)
{
var boundsPen = new Pen(Color.Cyan, 8.0f);
var bounds = Rectangle.FromLTRB(
map.Bounds.Left * Globals.TileWidth,
map.Bounds.Top * Globals.TileHeight,
map.Bounds.Right * Globals.TileWidth,
map.Bounds.Bottom * Globals.TileHeight
);
graphics.DrawRectangle(boundsPen, bounds);
}
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
navigationWidget.Dispose();
mapPanel.PreRender -= MapPanel_PreRender;
mapPanel.PostRender -= MapPanel_PostRender;
map.BasicSection.PropertyChanged -= BasicSection_PropertyChanged;
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
}

View file

@ -0,0 +1,384 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class WallsTool : ViewTool
{
private readonly TypeComboBox wallTypeComboBox;
private readonly MapPanel wallTypeMapPanel;
private readonly Dictionary<int, Overlay> undoOverlays = new Dictionary<int, Overlay>();
private readonly Dictionary<int, Overlay> redoOverlays = new Dictionary<int, Overlay>();
private Map previewMap;
protected override Map RenderMap => previewMap;
private bool placementMode;
private OverlayType selectedWallType;
private OverlayType SelectedWallType
{
get => selectedWallType;
set
{
if (selectedWallType != value)
{
if (placementMode && (selectedWallType != null))
{
mapPanel.Invalidate(map, navigationWidget.MouseCell);
}
selectedWallType = value;
wallTypeComboBox.SelectedValue = selectedWallType;
RefreshMapPanel();
}
}
}
public WallsTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, TypeComboBox wallTypeComboBox, MapPanel wallTypeMapPanel, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
previewMap = map;
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseUp += MapPanel_MouseUp;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += WallTool_KeyDown;
(this.mapPanel as Control).KeyUp += WallTool_KeyUp;
this.wallTypeComboBox = wallTypeComboBox;
this.wallTypeComboBox.SelectedIndexChanged += WallTypeComboBox_SelectedIndexChanged;
this.wallTypeMapPanel = wallTypeMapPanel;
this.wallTypeMapPanel.BackColor = Color.White;
this.wallTypeMapPanel.MaxZoom = 1;
navigationWidget.MouseCellChanged += MouseoverWidget_MouseCellChanged;
SelectedWallType = this.wallTypeComboBox.Types.First() as OverlayType;
UpdateStatus();
}
private void WallTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
SelectedWallType = wallTypeComboBox.SelectedValue as OverlayType;
}
private void WallTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void WallTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
AddWall(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveWall(navigationWidget.MouseCell);
}
}
else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right))
{
PickWall(navigationWidget.MouseCell);
}
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
if ((undoOverlays.Count > 0) || (redoOverlays.Count > 0))
{
CommitChange();
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void MouseoverWidget_MouseCellChanged(object sender, MouseCellChangedEventArgs e)
{
if (placementMode)
{
if (Control.MouseButtons == MouseButtons.Left)
{
AddWall(e.NewCell);
}
else if (Control.MouseButtons == MouseButtons.Right)
{
RemoveWall(e.NewCell);
}
if (SelectedWallType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.OldCell, new Size(1, 1)), 1, 1));
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(e.NewCell, new Size(1, 1)), 1, 1));
}
}
}
private void AddWall(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
if (SelectedWallType != null)
{
var overlay = new Overlay { Type = SelectedWallType, Icon = 0 };
if (map.Technos.CanAdd(cell, overlay) && map.Buildings.CanAdd(cell, overlay))
{
if (!undoOverlays.ContainsKey(cell))
{
undoOverlays[cell] = map.Overlay[cell];
}
map.Overlay[cell] = overlay;
redoOverlays[cell] = overlay;
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1));
plugin.Dirty = true;
}
}
}
}
private void RemoveWall(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var overlay = map.Overlay[cell];
if (overlay?.Type.IsWall ?? false)
{
if (!undoOverlays.ContainsKey(cell))
{
undoOverlays[cell] = map.Overlay[cell];
}
map.Overlay[cell] = null;
redoOverlays[cell] = null;
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1));
plugin.Dirty = true;
}
}
}
private void CommitChange()
{
var undoOverlays2 = new Dictionary<int, Overlay>(undoOverlays);
void undoAction(UndoRedoEventArgs e)
{
foreach (var kv in undoOverlays2)
{
e.Map.Overlay[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate(e.Map, undoOverlays2.Keys.Select(k =>
{
e.Map.Metrics.GetLocation(k, out Point location);
return Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1);
}));
}
var redoOverlays2 = new Dictionary<int, Overlay>(redoOverlays);
void redoAction(UndoRedoEventArgs e)
{
foreach (var kv in redoOverlays2)
{
e.Map.Overlay[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate(e.Map, redoOverlays2.Keys.Select(k =>
{
e.Map.Metrics.GetLocation(k, out Point location);
return Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1);
}));
}
undoOverlays.Clear();
redoOverlays.Clear();
url.Track(undoAction, redoAction);
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedWallType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedWallType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}
private void PickWall(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var overlay = map.Overlay[cell];
if ((overlay != null) && overlay.Type.IsWall)
{
SelectedWallType = overlay.Type;
}
}
}
private void RefreshMapPanel()
{
wallTypeMapPanel.MapImage = SelectedWallType?.Thumbnail;
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click drag to add walls, Right-Click drag to remove walls";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick wall";
}
}
protected override void PreRenderMap()
{
base.PreRenderMap();
previewMap = map.Clone();
if (placementMode)
{
var location = navigationWidget.MouseCell;
if (SelectedWallType != null)
{
if (previewMap.Metrics.GetCell(location, out int cell))
{
var overlay = new Overlay { Type = SelectedWallType, Icon = 0, Tint = Color.FromArgb(128, Color.White) };
if (previewMap.Technos.CanAdd(cell, overlay) && previewMap.Buildings.CanAdd(cell, overlay))
{
previewMap.Overlay[cell] = overlay;
mapPanel.Invalidate(previewMap, Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1));
}
}
}
}
}
protected override void PostRenderMap(Graphics graphics)
{
base.PostRenderMap(graphics);
var wallPen = new Pen(Color.Green, 4.0f);
foreach (var (cell, overlay) in previewMap.Overlay)
{
if (overlay.Type.IsWall)
{
previewMap.Metrics.GetLocation(cell, out Point topLeft);
var bounds = new Rectangle(new Point(topLeft.X * Globals.TileWidth, topLeft.Y * Globals.TileHeight), Globals.TileSize);
graphics.DrawRectangle(wallPen, bounds);
}
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseUp -= MapPanel_MouseUp;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= WallTool_KeyDown;
(mapPanel as Control).KeyUp -= WallTool_KeyUp;
wallTypeComboBox.SelectedIndexChanged -= WallTypeComboBox_SelectedIndexChanged;
navigationWidget.MouseCellChanged -= MouseoverWidget_MouseCellChanged;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}

View file

@ -0,0 +1,243 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// The Command & Conquer Map Editor and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using MobiusEditor.Widgets;
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
namespace MobiusEditor.Tools
{
public class WaypointsTool : ViewTool
{
private readonly ComboBox waypointCombo;
private (Waypoint waypoint, int? cell)? undoWaypoint;
private (Waypoint waypoint, int? cell)? redoWaypoint;
private bool placementMode;
public WaypointsTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, ComboBox waypointCombo, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
this.mapPanel.MouseDown += MapPanel_MouseDown;
this.mapPanel.MouseMove += MapPanel_MouseMove;
(this.mapPanel as Control).KeyDown += WaypointsTool_KeyDown;
(this.mapPanel as Control).KeyUp += WaypointsTool_KeyUp;
this.waypointCombo = waypointCombo;
UpdateStatus();
}
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
{
if (placementMode)
{
if (e.Button == MouseButtons.Left)
{
SetWaypoint(navigationWidget.MouseCell);
}
else if (e.Button == MouseButtons.Right)
{
RemoveWaypoint(navigationWidget.MouseCell);
}
}
else if ((e.Button == MouseButtons.Left) || (e.Button == MouseButtons.Right))
{
PickWaypoint(navigationWidget.MouseCell);
}
}
private void WaypointsTool_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
EnterPlacementMode();
}
}
private void WaypointsTool_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey)
{
ExitPlacementMode();
}
}
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (!placementMode && (Control.ModifierKeys == Keys.Shift))
{
EnterPlacementMode();
}
else if (placementMode && (Control.ModifierKeys == Keys.None))
{
ExitPlacementMode();
}
}
private void SetWaypoint(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var waypoint = map.Waypoints[waypointCombo.SelectedIndex];
if (waypoint.Cell != cell)
{
if (undoWaypoint == null)
{
undoWaypoint = (waypoint, waypoint.Cell);
}
else if (undoWaypoint.Value.cell == cell)
{
undoWaypoint = null;
}
waypoint.Cell = cell;
redoWaypoint = (waypoint, waypoint.Cell);
CommitChange();
mapPanel.Invalidate();
plugin.Dirty = true;
}
}
}
private void RemoveWaypoint(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
var waypoint = map.Waypoints.Where(w => w.Cell == cell).FirstOrDefault();
if (waypoint != null)
{
if (undoWaypoint == null)
{
undoWaypoint = (waypoint, waypoint.Cell);
}
waypoint.Cell = null;
redoWaypoint = (waypoint, null);
CommitChange();
mapPanel.Invalidate();
plugin.Dirty = true;
}
}
}
private void EnterPlacementMode()
{
if (placementMode)
{
return;
}
placementMode = true;
UpdateStatus();
}
private void ExitPlacementMode()
{
if (!placementMode)
{
return;
}
placementMode = false;
UpdateStatus();
}
private void PickWaypoint(Point location)
{
if (map.Metrics.GetCell(location, out int cell))
{
for (var i = 0; i < map.Waypoints.Length; ++i)
{
if (map.Waypoints[i].Cell == cell)
{
waypointCombo.SelectedIndex = i;
break;
}
}
}
}
private void CommitChange()
{
var undoWaypoint2 = undoWaypoint;
void undoAction(UndoRedoEventArgs e)
{
undoWaypoint2.Value.waypoint.Cell = undoWaypoint2.Value.cell;
mapPanel.Invalidate();
}
var redoWaypoint2 = redoWaypoint;
void redoAction(UndoRedoEventArgs e)
{
redoWaypoint2.Value.waypoint.Cell = redoWaypoint2.Value.cell;
mapPanel.Invalidate();
}
undoWaypoint = null;
redoWaypoint = null;
url.Track(undoAction, redoAction);
}
private void UpdateStatus()
{
if (placementMode)
{
statusLbl.Text = "Left-Click to set cell waypoint, Right-Click to clear cell waypoint";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick cell waypoint";
}
}
#region IDisposable Support
private bool disposedValue = false;
protected override void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
mapPanel.MouseDown -= MapPanel_MouseDown;
mapPanel.MouseMove -= MapPanel_MouseMove;
(mapPanel as Control).KeyDown -= WaypointsTool_KeyDown;
(mapPanel as Control).KeyUp -= WaypointsTool_KeyUp;
}
disposedValue = true;
}
base.Dispose(disposing);
}
#endregion
}
}