CnC_Remastered_Collection/CnCTDRAMapEditor/MainForm.cs
PG-SteveT e37e174be1 C&C Remastered Map Editor
Initial commit of C&C Remastered Map Editor code
2020-09-10 11:12:58 -07:00

1416 lines
49 KiB
C#

//
// 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.Dialogs;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Tools;
using MobiusEditor.Tools.Dialogs;
using MobiusEditor.Utility;
using Steamworks;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MobiusEditor
{
public partial class MainForm : Form
{
[Flags]
private enum ToolType
{
None = 0,
Map = 1 << 0,
Smudge = 1 << 1,
Overlay = 1 << 2,
Terrain = 1 << 3,
Infantry = 1 << 4,
Unit = 1 << 5,
Building = 1 << 6,
Resources = 1 << 7,
Wall = 1 << 8,
Waypoint = 1 << 9,
CellTrigger = 1 << 10
}
private static readonly ToolType[] toolTypes;
private ToolType availableToolTypes = ToolType.None;
private ToolType activeToolType = ToolType.None;
private ToolType ActiveToolType
{
get => activeToolType;
set
{
var firstAvailableTool = value;
if ((availableToolTypes & firstAvailableTool) == ToolType.None)
{
var otherAvailableToolTypes = toolTypes.Where(t => (availableToolTypes & t) != ToolType.None);
firstAvailableTool = otherAvailableToolTypes.Any() ? otherAvailableToolTypes.First() : ToolType.None;
}
if (activeToolType != firstAvailableTool)
{
activeToolType = firstAvailableTool;
RefreshActiveTool();
}
}
}
private MapLayerFlag activeLayers;
public MapLayerFlag ActiveLayers
{
get => activeLayers;
set
{
if (activeLayers != value)
{
activeLayers = value;
if (activeTool != null)
{
activeTool.Layers = ActiveLayers;
}
}
}
}
private ITool activeTool;
private Form activeToolForm;
private IGamePlugin plugin;
private string filename;
private readonly MRU mru;
private readonly UndoRedoList<UndoRedoEventArgs> url = new UndoRedoList<UndoRedoEventArgs>();
private readonly Timer steamUpdateTimer = new Timer();
static MainForm()
{
toolTypes = ((IEnumerable<ToolType>)Enum.GetValues(typeof(ToolType))).Where(t => t != ToolType.None).ToArray();
}
public MainForm()
{
InitializeComponent();
mru = new MRU("Software\\Petroglyph\\CnCRemasteredEditor", 10, fileRecentFilesMenuItem);
mru.FileSelected += Mru_FileSelected;
foreach (ToolStripButton toolStripButton in mainToolStrip.Items)
{
toolStripButton.MouseMove += mainToolStrip_MouseMove;
}
#if !DEVELOPER
fileExportMenuItem.Visible = false;
developerToolStripMenuItem.Visible = false;
#endif
url.Tracked += UndoRedo_Updated;
url.Undone += UndoRedo_Updated;
url.Redone += UndoRedo_Updated;
UpdateUndoRedo();
steamUpdateTimer.Interval = 500;
steamUpdateTimer.Tick += SteamUpdateTimer_Tick;
}
private void SteamUpdateTimer_Tick(object sender, EventArgs e)
{
if (SteamworksUGC.IsInit)
{
SteamworksUGC.Service();
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
RefreshAvailableTools();
UpdateVisibleLayers();
filePublishMenuItem.Visible = SteamworksUGC.IsInit;
steamUpdateTimer.Start();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
steamUpdateTimer.Stop();
steamUpdateTimer.Dispose();
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Q)
{
mapToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.W)
{
smudgeToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.E)
{
overlayToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.R)
{
terrainToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.T)
{
infantryToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.Y)
{
unitToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.A)
{
buildingToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.S)
{
resourcesToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.D)
{
wallsToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.F)
{
waypointsToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.G)
{
cellTriggersToolStripButton.PerformClick();
return true;
}
else if (keyData == (Keys.Control | Keys.Z))
{
if (editUndoMenuItem.Enabled)
{
editUndoMenuItem_Click(this, new EventArgs());
}
return true;
}
else if (keyData == (Keys.Control | Keys.Y))
{
if (editRedoMenuItem.Enabled)
{
editRedoMenuItem_Click(this, new EventArgs());
}
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void UpdateUndoRedo()
{
editUndoMenuItem.Enabled = url.CanUndo;
editRedoMenuItem.Enabled = url.CanRedo;
}
private void UndoRedo_Updated(object sender, EventArgs e)
{
UpdateUndoRedo();
}
private void fileNewMenuItem_Click(object sender, EventArgs e)
{
if (!PromptSaveMap())
{
return;
}
NewMapDialog nmd = new NewMapDialog();
if (nmd.ShowDialog() == DialogResult.OK)
{
if (plugin != null)
{
plugin.Map.Triggers.CollectionChanged -= Triggers_CollectionChanged;
plugin.Dispose();
}
plugin = null;
Globals.TheTilesetManager.Reset();
Globals.TheTextureManager.Reset();
if (nmd.GameType == GameType.TiberianDawn)
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
plugin = new TiberianDawn.GamePlugin();
plugin.New(nmd.TheaterName);
}
else if (nmd.GameType == GameType.RedAlert)
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
plugin = new RedAlert.GamePlugin();
plugin.New(nmd.TheaterName);
}
if (SteamworksUGC.IsInit)
{
plugin.Map.BasicSection.Author = SteamFriends.GetPersonaName();
}
plugin.Map.Triggers.CollectionChanged += Triggers_CollectionChanged;
mapPanel.MapImage = plugin.MapImage;
filename = null;
Text = "CnC TDRA Map Editor";
url.Clear();
ClearActiveTool();
RefreshAvailableTools();
RefreshActiveTool();
}
}
private void fileOpenMenuItem_Click(object sender, EventArgs e)
{
if (!PromptSaveMap())
{
return;
}
var pgmFilter =
#if DEVELOPER
"|PGM files (*.pgm)|*.pgm"
#else
string.Empty
#endif
;
OpenFileDialog ofd = new OpenFileDialog
{
AutoUpgradeEnabled = false,
RestoreDirectory = true
};
ofd.Filter = "Tiberian Dawn files (*.ini;*.bin)|*.ini;*.bin|Red Alert files (*.mpr)|*.mpr" + pgmFilter + "|All files (*.*)|*.*";
if (plugin != null)
{
switch (plugin.GameType)
{
case GameType.TiberianDawn:
ofd.InitialDirectory = TiberianDawn.Constants.SaveDirectory;
ofd.FilterIndex = 1;
break;
case GameType.RedAlert:
ofd.InitialDirectory = RedAlert.Constants.SaveDirectory;
ofd.FilterIndex = 2;
break;
}
}
else
{
ofd.InitialDirectory = Globals.RootSaveDirectory;
}
if (ofd.ShowDialog() == DialogResult.OK)
{
var fileInfo = new FileInfo(ofd.FileName);
if (LoadFile(fileInfo.FullName))
{
mru.Add(fileInfo);
}
else
{
mru.Remove(fileInfo);
MessageBox.Show(string.Format("Error loading {0}.", ofd.FileName), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private void fileSaveMenuItem_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
if (string.IsNullOrEmpty(filename))
{
fileSaveAsMenuItem.PerformClick();
}
else
{
var fileInfo = new FileInfo(filename);
if (SaveFile(fileInfo.FullName))
{
mru.Add(fileInfo);
}
else
{
mru.Remove(fileInfo);
}
}
}
private void fileSaveAsMenuItem_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
SaveFileDialog sfd = new SaveFileDialog
{
AutoUpgradeEnabled = false,
RestoreDirectory = true
};
var filters = new List<string>();
switch (plugin.GameType)
{
case GameType.TiberianDawn:
filters.Add("Tiberian Dawn files (*.ini;*.bin)|*.ini;*.bin");
sfd.InitialDirectory = TiberianDawn.Constants.SaveDirectory;
break;
case GameType.RedAlert:
filters.Add("Red Alert files (*.mpr)|*.mpr");
sfd.InitialDirectory = RedAlert.Constants.SaveDirectory;
break;
}
filters.Add("All files (*.*)|*.*");
sfd.Filter = string.Join("|", filters);
if (!string.IsNullOrEmpty(filename))
{
sfd.InitialDirectory = Path.GetDirectoryName(filename);
sfd.FileName = Path.GetFileName(filename);
}
if (sfd.ShowDialog() == DialogResult.OK)
{
var fileInfo = new FileInfo(sfd.FileName);
if (SaveFile(fileInfo.FullName))
{
mru.Add(fileInfo);
}
else
{
mru.Remove(fileInfo);
}
}
}
private void fileExportMenuItem_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
SaveFileDialog sfd = new SaveFileDialog
{
AutoUpgradeEnabled = false,
RestoreDirectory = true
};
sfd.Filter = "MEG files (*.meg)|*.meg";
if (sfd.ShowDialog() == DialogResult.OK)
{
plugin.Save(sfd.FileName, FileType.MEG);
}
}
private void fileExitMenuItem_Click(object sender, EventArgs e)
{
Close();
}
private void editUndoMenuItem_Click(object sender, EventArgs e)
{
if (url.CanUndo)
{
url.Undo(new UndoRedoEventArgs(mapPanel, plugin.Map));
}
}
private void editRedoMenuItem_Click(object sender, EventArgs e)
{
if (url.CanRedo)
{
url.Redo(new UndoRedoEventArgs(mapPanel, plugin.Map));
}
}
private void settingsMapSettingsMenuItem_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
var basicSettings = new PropertyTracker<BasicSection>(plugin.Map.BasicSection);
var briefingSettings = new PropertyTracker<BriefingSection>(plugin.Map.BriefingSection);
var houseSettingsTrackers = plugin.Map.Houses.ToDictionary(h => h, h => new PropertyTracker<House>(h));
MapSettingsDialog msd = new MapSettingsDialog(plugin, basicSettings, briefingSettings, houseSettingsTrackers);
if (msd.ShowDialog() == DialogResult.OK)
{
basicSettings.Commit();
briefingSettings.Commit();
foreach (var houseSettingsTracker in houseSettingsTrackers.Values)
{
houseSettingsTracker.Commit();
}
plugin.Dirty = true;
}
}
private void settingsTeamTypesMenuItem_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
int maxTeams = 0;
switch (plugin.GameType)
{
case GameType.TiberianDawn:
{
maxTeams = TiberianDawn.Constants.MaxTeams;
}
break;
case GameType.RedAlert:
{
maxTeams = RedAlert.Constants.MaxTeams;
}
break;
}
TeamTypesDialog ttd = new TeamTypesDialog(plugin, maxTeams);
if (ttd.ShowDialog() == DialogResult.OK)
{
plugin.Map.TeamTypes.Clear();
plugin.Map.TeamTypes.AddRange(ttd.TeamTypes.Select(t => t.Clone()));
plugin.Dirty = true;
}
}
private void settingsTriggersMenuItem_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
int maxTriggers = 0;
switch (plugin.GameType)
{
case GameType.TiberianDawn:
{
maxTriggers = TiberianDawn.Constants.MaxTriggers;
}
break;
case GameType.RedAlert:
{
maxTriggers = RedAlert.Constants.MaxTriggers;
}
break;
}
TriggersDialog td = new TriggersDialog(plugin, maxTriggers);
if (td.ShowDialog() == DialogResult.OK)
{
var oldTriggers =
from leftTrigger in plugin.Map.Triggers
join rightTrigger in td.Triggers
on leftTrigger.Name equals rightTrigger.Name into result
where result.Count() == 0
select leftTrigger;
var newTriggers =
from leftTrigger in td.Triggers
join rightTrigger in plugin.Map.Triggers
on leftTrigger.Name equals rightTrigger.Name into result
where result.Count() == 0
select leftTrigger;
var sameTriggers =
from leftTrigger in plugin.Map.Triggers
join rightTrigger in td.Triggers
on leftTrigger.Name equals rightTrigger.Name
select new
{
OldTrigger = leftTrigger,
NewTrigger = rightTrigger
};
foreach (var oldTrigger in oldTriggers.ToArray())
{
plugin.Map.Triggers.Remove(oldTrigger);
}
foreach (var newTrigger in newTriggers.ToArray())
{
plugin.Map.Triggers.Add(newTrigger.Clone());
}
foreach (var item in sameTriggers.ToArray())
{
plugin.Map.Triggers.Add(item.NewTrigger.Clone());
plugin.Map.Triggers.Remove(item.OldTrigger);
}
plugin.Dirty = true;
}
}
private void Mru_FileSelected(object sender, FileInfo e)
{
if (!PromptSaveMap())
{
return;
}
if (LoadFile(e.FullName))
{
mru.Add(e);
}
else
{
mru.Remove(e);
MessageBox.Show(string.Format("Error loading {0}.", e.FullName), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void mapPanel_MouseMove(object sender, MouseEventArgs e)
{
if (plugin != null)
{
var mapPoint = mapPanel.ClientToMap(e.Location);
var location = new Point((int)Math.Floor((double)mapPoint.X / Globals.TileWidth), (int)Math.Floor((double)mapPoint.Y / Globals.TileHeight));
if (plugin.Map.Metrics.GetCell(location, out int cell))
{
var sb = new StringBuilder();
sb.AppendFormat("X = {0}, Y = {1}, Cell = {2}", location.X, location.Y, cell);
var template = plugin.Map.Templates[cell];
var templateType = template?.Type;
if (templateType != null)
{
sb.AppendFormat(", Template = {0} ({1})", templateType.DisplayName, template.Icon);
}
var smudge = plugin.Map.Smudge[cell];
var smudgeType = smudge?.Type;
if (smudgeType != null)
{
sb.AppendFormat(", Smudge = {0}", smudgeType.DisplayName);
}
var overlay = plugin.Map.Overlay[cell];
var overlayType = overlay?.Type;
if (overlayType != null)
{
sb.AppendFormat(", Overlay = {0}", overlayType.DisplayName);
}
var terrain = plugin.Map.Technos[location] as Terrain;
var terrainType = terrain?.Type;
if (terrainType != null)
{
sb.AppendFormat(", Terrain = {0}", terrainType.DisplayName);
}
if (plugin.Map.Technos[location] is InfantryGroup infantryGroup)
{
var subPixel = new Point(
(mapPoint.X * Globals.PixelWidth / Globals.TileWidth) % Globals.PixelWidth,
(mapPoint.Y * Globals.PixelHeight / Globals.TileHeight) % Globals.PixelHeight
);
var i = InfantryGroup.ClosestStoppingTypes(subPixel).Cast<int>().First();
if (infantryGroup.Infantry[i] != null)
{
sb.AppendFormat(", Infantry = {0}", infantryGroup.Infantry[i].Type.DisplayName);
}
}
var unit = plugin.Map.Technos[location] as Unit;
var unitType = unit?.Type;
if (unitType != null)
{
sb.AppendFormat(", Unit = {0}", unitType.DisplayName);
}
var building = plugin.Map.Technos[location] as Building;
var buildingType = building?.Type;
if (buildingType != null)
{
sb.AppendFormat(", Building = {0}", buildingType.DisplayName);
}
cellStatusLabel.Text = sb.ToString();
}
else
{
cellStatusLabel.Text = string.Empty;
}
}
}
private bool LoadFile(string loadFilename)
{
FileType fileType = FileType.None;
switch (Path.GetExtension(loadFilename).ToLower())
{
case ".ini":
case ".mpr":
fileType = FileType.INI;
break;
case ".bin":
fileType = FileType.BIN;
break;
#if DEVELOPER
case ".pgm":
fileType = FileType.PGM;
break;
#endif
}
if (fileType == FileType.None)
{
return false;
}
GameType gameType = GameType.None;
switch (fileType)
{
case FileType.INI:
{
var ini = new INI();
try
{
using (var reader = new StreamReader(loadFilename))
{
ini.Parse(reader);
}
}
catch (FileNotFoundException)
{
return false;
}
gameType = File.Exists(Path.ChangeExtension(loadFilename, ".bin")) ? GameType.TiberianDawn : GameType.RedAlert;
}
break;
case FileType.BIN:
gameType = GameType.TiberianDawn;
break;
#if DEVELOPER
case FileType.PGM:
{
try
{
using (var megafile = new Megafile(loadFilename))
{
if (megafile.Any(f => Path.GetExtension(f).ToLower() == ".mpr"))
{
gameType = GameType.RedAlert;
}
else
{
gameType = GameType.TiberianDawn;
}
}
}
catch (FileNotFoundException)
{
return false;
}
}
break;
#endif
}
if (gameType == GameType.None)
{
return false;
}
if (plugin != null)
{
plugin.Map.Triggers.CollectionChanged -= Triggers_CollectionChanged;
plugin.Dispose();
}
plugin = null;
Globals.TheTilesetManager.Reset();
Globals.TheTextureManager.Reset();
switch (gameType)
{
case GameType.TiberianDawn:
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
plugin = new TiberianDawn.GamePlugin();
}
break;
case GameType.RedAlert:
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
plugin = new RedAlert.GamePlugin();
}
break;
}
try
{
var errors = plugin.Load(loadFilename, fileType).ToArray();
if (errors.Length > 0)
{
ErrorMessageBox errorMessageBox = new ErrorMessageBox { Errors = errors };
errorMessageBox.ShowDialog();
}
}
catch (Exception)
{
#if DEVELOPER
throw;
#else
return false;
#endif
}
plugin.Map.Triggers.CollectionChanged += Triggers_CollectionChanged;
mapPanel.MapImage = plugin.MapImage;
plugin.Dirty = false;
filename = loadFilename;
Text = string.Format("CnC TDRA Map Editor - {0}", filename);
url.Clear();
ClearActiveTool();
RefreshAvailableTools();
RefreshActiveTool();
return true;
}
private bool SaveFile(string saveFilename)
{
FileType fileType = FileType.None;
switch (Path.GetExtension(saveFilename).ToLower())
{
case ".ini":
case ".mpr":
fileType = FileType.INI;
break;
case ".bin":
fileType = FileType.BIN;
break;
}
if (fileType == FileType.None)
{
return false;
}
if (string.IsNullOrEmpty(plugin.Map.SteamSection.Title))
{
plugin.Map.SteamSection.Title = plugin.Map.BasicSection.Name;
}
if (!plugin.Save(saveFilename, fileType))
{
return false;
}
if (new FileInfo(saveFilename).Length > Globals.MaxMapSize)
{
MessageBox.Show(string.Format("Map file exceeds the maximum size of {0} bytes.", Globals.MaxMapSize), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
plugin.Dirty = false;
filename = saveFilename;
Text = string.Format("CnC TDRA Map Editor - {0}", filename);
return true;
}
private void RefreshAvailableTools()
{
availableToolTypes = ToolType.None;
if (plugin != null)
{
availableToolTypes |= ToolType.Waypoint;
if (plugin.Map.TemplateTypes.Any()) availableToolTypes |= ToolType.Map;
if (plugin.Map.SmudgeTypes.Any()) availableToolTypes |= ToolType.Smudge;
if (plugin.Map.OverlayTypes.Any(t => t.IsPlaceable && ((t.Theaters == null) || t.Theaters.Contains(plugin.Map.Theater)))) availableToolTypes |= ToolType.Overlay;
if (plugin.Map.TerrainTypes.Any(t => t.Theaters.Contains(plugin.Map.Theater))) availableToolTypes |= ToolType.Terrain;
if (plugin.Map.InfantryTypes.Any()) availableToolTypes |= ToolType.Infantry;
if (plugin.Map.UnitTypes.Any()) availableToolTypes |= ToolType.Unit;
if (plugin.Map.BuildingTypes.Any()) availableToolTypes |= ToolType.Building;
if (plugin.Map.OverlayTypes.Any(t => t.IsResource)) availableToolTypes |= ToolType.Resources;
if (plugin.Map.OverlayTypes.Any(t => t.IsWall)) availableToolTypes |= ToolType.Wall;
if (plugin.Map.Triggers.Any()) availableToolTypes |= ToolType.CellTrigger;
}
mapToolStripButton.Enabled = (availableToolTypes & ToolType.Map) != ToolType.None;
smudgeToolStripButton.Enabled = (availableToolTypes & ToolType.Smudge) != ToolType.None;
overlayToolStripButton.Enabled = (availableToolTypes & ToolType.Overlay) != ToolType.None;
terrainToolStripButton.Enabled = (availableToolTypes & ToolType.Terrain) != ToolType.None;
infantryToolStripButton.Enabled = (availableToolTypes & ToolType.Infantry) != ToolType.None;
unitToolStripButton.Enabled = (availableToolTypes & ToolType.Unit) != ToolType.None;
buildingToolStripButton.Enabled = (availableToolTypes & ToolType.Building) != ToolType.None;
resourcesToolStripButton.Enabled = (availableToolTypes & ToolType.Resources) != ToolType.None;
wallsToolStripButton.Enabled = (availableToolTypes & ToolType.Wall) != ToolType.None;
waypointsToolStripButton.Enabled = (availableToolTypes & ToolType.Waypoint) != ToolType.None;
cellTriggersToolStripButton.Enabled = (availableToolTypes & ToolType.CellTrigger) != ToolType.None;
ActiveToolType = activeToolType;
}
private void ClearActiveTool()
{
activeTool?.Dispose();
activeTool = null;
if (activeToolForm != null)
{
activeToolForm.ResizeEnd -= ActiveToolForm_ResizeEnd;
activeToolForm.Close();
activeToolForm = null;
}
toolStatusLabel.Text = string.Empty;
}
private void RefreshActiveTool()
{
if (plugin == null)
{
return;
}
if (activeTool == null)
{
activeLayers = MapLayerFlag.None;
}
ClearActiveTool();
switch (ActiveToolType)
{
case ToolType.Map:
{
TemplateToolDialog toolDialog = new TemplateToolDialog();
activeTool = new TemplateTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TemplateTypeListView, toolDialog.TemplateTypeMapPanel, mouseToolTip, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
} break;
case ToolType.Smudge:
{
GenericToolDialog toolDialog = new GenericToolDialog
{
Text = "Smudge"
};
toolDialog.GenericTypeComboBox.Types = plugin.Map.SmudgeTypes.Where(t => (t.Flag & SmudgeTypeFlag.Bib) == SmudgeTypeFlag.None).OrderBy(t => t.Name);
activeTool = new SmudgeTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.GenericTypeComboBox, toolDialog.GenericTypeMapPanel, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Overlay:
{
GenericToolDialog toolDialog = new GenericToolDialog
{
Text = "Overlay"
};
toolDialog.GenericTypeComboBox.Types = plugin.Map.OverlayTypes.Where(t => t.IsPlaceable && ((t.Theaters == null) || t.Theaters.Contains(plugin.Map.Theater))).OrderBy(t => t.Name);
activeTool = new OverlaysTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.GenericTypeComboBox, toolDialog.GenericTypeMapPanel, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Resources:
{
ResourcesToolDialog toolDialog = new ResourcesToolDialog();
activeTool = new ResourcesTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TotalResourcesLbl, toolDialog.ResourceBrushSizeNud, toolDialog.GemsCheckBox, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Terrain:
{
TerrainToolDialog toolDialog = new TerrainToolDialog(plugin);
toolDialog.TerrainTypeComboBox.Types = plugin.Map.TerrainTypes.Where(t => t.Theaters.Contains(plugin.Map.Theater)).OrderBy(t => t.Name);
activeTool = new TerrainTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TerrainTypeComboBox, toolDialog.TerrainTypeMapPanel, toolDialog.TerrainProperties, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Infantry:
{
ObjectToolDialog toolDialog = new ObjectToolDialog(plugin)
{
Text = "Infantry"
};
toolDialog.ObjectTypeComboBox.Types = plugin.Map.InfantryTypes.OrderBy(t => t.Name);
activeTool = new InfantryTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.ObjectTypeComboBox, toolDialog.ObjectTypeMapPanel, toolDialog.ObjectProperties, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Unit:
{
ObjectToolDialog toolDialog = new ObjectToolDialog(plugin)
{
Text = "Units"
};
toolDialog.ObjectTypeComboBox.Types = plugin.Map.UnitTypes
.Where(t => !t.IsFixedWing)
.OrderBy(t => t.Name);
activeTool = new UnitTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.ObjectTypeComboBox, toolDialog.ObjectTypeMapPanel, toolDialog.ObjectProperties, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Building:
{
ObjectToolDialog toolDialog = new ObjectToolDialog(plugin)
{
Text = "Structures"
};
toolDialog.ObjectTypeComboBox.Types = plugin.Map.BuildingTypes
.Where(t => (t.Theaters == null) || t.Theaters.Contains(plugin.Map.Theater))
.OrderBy(t => t.IsFake)
.ThenBy(t => t.Name);
activeTool = new BuildingTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.ObjectTypeComboBox, toolDialog.ObjectTypeMapPanel, toolDialog.ObjectProperties, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Wall:
{
GenericToolDialog toolDialog = new GenericToolDialog
{
Text = "Walls"
};
toolDialog.GenericTypeComboBox.Types = plugin.Map.OverlayTypes.Where(t => t.IsWall).OrderBy(t => t.Name);
activeTool = new WallsTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.GenericTypeComboBox, toolDialog.GenericTypeMapPanel, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.Waypoint:
{
WaypointsToolDialog toolDialog = new WaypointsToolDialog();
toolDialog.WaypointCombo.DataSource = plugin.Map.Waypoints.Select(w => w.Name).ToArray();
activeTool = new WaypointsTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.WaypointCombo, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
case ToolType.CellTrigger:
{
CellTriggersToolDialog toolDialog = new CellTriggersToolDialog();
toolDialog.TriggerCombo.DataSource = plugin.Map.Triggers.Select(t => t.Name).ToArray();
activeTool = new CellTriggersTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TriggerCombo, plugin, url);
activeToolForm = toolDialog;
activeToolForm.Show(this);
}
break;
}
if (activeToolForm != null)
{
activeToolForm.ResizeEnd += ActiveToolForm_ResizeEnd;
clampActiveToolForm();
}
switch (plugin.GameType)
{
case GameType.TiberianDawn:
mapPanel.MaxZoom = 8;
mapPanel.ZoomStep = 1;
break;
case GameType.RedAlert:
mapPanel.MaxZoom = 16;
mapPanel.ZoomStep = 2;
break;
}
mapToolStripButton.Checked = ActiveToolType == ToolType.Map;
smudgeToolStripButton.Checked = ActiveToolType == ToolType.Smudge;
overlayToolStripButton.Checked = ActiveToolType == ToolType.Overlay;
terrainToolStripButton.Checked = ActiveToolType == ToolType.Terrain;
infantryToolStripButton.Checked = ActiveToolType == ToolType.Infantry;
unitToolStripButton.Checked = ActiveToolType == ToolType.Unit;
buildingToolStripButton.Checked = ActiveToolType == ToolType.Building;
resourcesToolStripButton.Checked = ActiveToolType == ToolType.Resources;
wallsToolStripButton.Checked = ActiveToolType == ToolType.Wall;
waypointsToolStripButton.Checked = ActiveToolType == ToolType.Waypoint;
cellTriggersToolStripButton.Checked = ActiveToolType == ToolType.CellTrigger;
Focus();
UpdateVisibleLayers();
mapPanel.Invalidate();
}
private void clampActiveToolForm()
{
if (activeToolForm == null)
{
return;
}
Rectangle bounds = activeToolForm.DesktopBounds;
Rectangle workingArea = Screen.FromControl(this).WorkingArea;
if (bounds.Right > workingArea.Right)
{
bounds.X = workingArea.Right - bounds.Width;
}
if (bounds.X < workingArea.Left)
{
bounds.X = workingArea.Left;
}
if (bounds.Bottom > workingArea.Bottom)
{
bounds.Y = workingArea.Bottom - bounds.Height;
}
if (bounds.Y < workingArea.Top)
{
bounds.Y = workingArea.Top;
}
activeToolForm.DesktopBounds = bounds;
}
private void ActiveToolForm_ResizeEnd(object sender, EventArgs e)
{
clampActiveToolForm();
}
private void Triggers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
RefreshAvailableTools();
}
private void mainToolStripButton_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
if (sender == mapToolStripButton)
{
ActiveToolType = ToolType.Map;
}
else if (sender == smudgeToolStripButton)
{
ActiveToolType = ToolType.Smudge;
}
else if (sender == overlayToolStripButton)
{
ActiveToolType = ToolType.Overlay;
}
else if (sender == terrainToolStripButton)
{
ActiveToolType = ToolType.Terrain;
}
else if (sender == infantryToolStripButton)
{
ActiveToolType = ToolType.Infantry;
}
else if (sender == unitToolStripButton)
{
ActiveToolType = ToolType.Unit;
}
else if (sender == buildingToolStripButton)
{
ActiveToolType = ToolType.Building;
}
else if (sender == resourcesToolStripButton)
{
ActiveToolType = ToolType.Resources;
}
else if (sender == wallsToolStripButton)
{
ActiveToolType = ToolType.Wall;
}
else if (sender == waypointsToolStripButton)
{
ActiveToolType = ToolType.Waypoint;
}
else if (sender == cellTriggersToolStripButton)
{
ActiveToolType = ToolType.CellTrigger;
}
}
private void UpdateVisibleLayers()
{
MapLayerFlag layers = MapLayerFlag.All;
if (!viewLayersBoundariesMenuItem.Checked)
{
layers &= ~MapLayerFlag.Boundaries;
}
if (!viewLayersOverlayMenuItem.Checked)
{
layers &= ~MapLayerFlag.OverlayAll;
}
if (!viewLayersTerrainMenuItem.Checked)
{
layers &= ~MapLayerFlag.Terrain;
}
if (!viewLayersWaypointsMenuItem.Checked)
{
layers &= ~MapLayerFlag.Waypoints;
}
if (!viewLayersCellTriggersMenuItem.Checked)
{
layers &= ~MapLayerFlag.CellTriggers;
}
if (!viewLayersObjectTriggersMenuItem.Checked)
{
layers &= ~MapLayerFlag.TechnoTriggers;
}
ActiveLayers = layers;
}
private void viewLayersMenuItem_CheckedChanged(object sender, EventArgs e)
{
UpdateVisibleLayers();
}
private void toolTabControl_Selected(object sender, TabControlEventArgs e)
{
if (plugin == null)
{
return;
}
}
private void developerGenerateMapPreviewMenuItem_Click(object sender, EventArgs e)
{
#if DEVELOPER
if ((plugin == null) || string.IsNullOrEmpty(filename))
{
return;
}
plugin.Map.GenerateMapPreview().Save(Path.ChangeExtension(filename, ".tga"));
#endif
}
private void developerGoToINIMenuItem_Click(object sender, EventArgs e)
{
#if DEVELOPER
if ((plugin == null) || string.IsNullOrEmpty(filename))
{
return;
}
var path = Path.ChangeExtension(filename, ".mpr");
if (!File.Exists(path))
{
path = Path.ChangeExtension(filename, ".ini");
}
try
{
Process.Start(path);
}
catch (Win32Exception)
{
Process.Start("notepad.exe", path);
}
catch (Exception) { }
#endif
}
private void developerGenerateMapPreviewDirectoryMenuItem_Click(object sender, EventArgs e)
{
#if DEVELOPER
FolderBrowserDialog fbd = new FolderBrowserDialog
{
ShowNewFolderButton = false
};
if (fbd.ShowDialog() == DialogResult.OK)
{
var extensions = new string[] { ".ini", ".mpr" };
foreach (var file in Directory.EnumerateFiles(fbd.SelectedPath).Where(file => extensions.Contains(Path.GetExtension(file).ToLower())))
{
GameType gameType = GameType.None;
var ini = new INI();
using (var reader = new StreamReader(file))
{
ini.Parse(reader);
}
gameType = ini.Sections.Contains("MapPack") ? GameType.RedAlert : GameType.TiberianDawn;
if (gameType == GameType.None)
{
continue;
}
IGamePlugin plugin = null;
switch (gameType)
{
case GameType.TiberianDawn:
{
plugin = new TiberianDawn.GamePlugin(false);
}
break;
case GameType.RedAlert:
{
plugin = new RedAlert.GamePlugin(false);
}
break;
}
plugin.Load(file, FileType.INI);
plugin.Map.GenerateMapPreview().Save(Path.ChangeExtension(file, ".tga"));
plugin.Dispose();
}
}
#endif
}
private void developerDebugShowOverlapCellsMenuItem_CheckedChanged(object sender, EventArgs e)
{
#if DEVELOPER
Globals.Developer.ShowOverlapCells = developerDebugShowOverlapCellsMenuItem.Checked;
#endif
}
private void filePublishMenuItem_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
if (!PromptSaveMap())
{
return;
}
if (plugin.Dirty)
{
MessageBox.Show("Map must be saved before publishing.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (new FileInfo(filename).Length > Globals.MaxMapSize)
{
return;
}
using (var sd = new SteamDialog(plugin))
{
sd.ShowDialog();
}
fileSaveMenuItem.PerformClick();
}
private void mainToolStrip_MouseMove(object sender, MouseEventArgs e)
{
mainToolStrip.Focus();
}
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = !PromptSaveMap();
}
private bool PromptSaveMap()
{
bool cancel = false;
if (plugin?.Dirty ?? false)
{
var message = string.IsNullOrEmpty(filename) ? "Save new map?" : string.Format("Save map '{0}'?", filename);
var result = MessageBox.Show(message, "Save", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
switch (result)
{
case DialogResult.Yes:
{
if (string.IsNullOrEmpty(filename))
{
fileSaveAsMenuItem.PerformClick();
}
else
{
fileSaveMenuItem.PerformClick();
}
}
break;
case DialogResult.No:
break;
case DialogResult.Cancel:
cancel = true;
break;
}
}
return !cancel;
}
}
}