1339 lines
44 KiB
C#
Raw Normal View History

2020-09-11 23:46:04 +03:00
//
// 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;
2020-09-11 23:46:04 +03:00
using MobiusEditor.Dialogs;
using MobiusEditor.Event;
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Tools.Dialogs;
using MobiusEditor.Utility;
using Steamworks;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MobiusEditor
{
public partial class MainForm : Form
{
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;
2020-09-14 13:40:09 +03:00
// Save and re-use tool instances
private Dictionary<ToolType, IToolDialog> toolForms;
private ViewToolStripButton[] viewToolStripButtons;
2020-09-14 13:40:09 +03:00
2020-09-11 23:46:04 +03:00
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(String fileToOpen)
2020-09-11 23:46:04 +03:00
{
this.filename = fileToOpen;
2020-09-11 23:46:04 +03:00
InitializeComponent();
2020-09-14 13:40:09 +03:00
toolForms = new Dictionary<ToolType, IToolDialog>();
viewToolStripButtons = new ViewToolStripButton[]
{
mapToolStripButton,
smudgeToolStripButton,
overlayToolStripButton,
terrainToolStripButton,
infantryToolStripButton,
unitToolStripButton,
buildingToolStripButton,
resourcesToolStripButton,
wallsToolStripButton,
waypointsToolStripButton,
cellTriggersToolStripButton
};
2020-09-14 13:40:09 +03:00
2020-09-11 23:46:04 +03:00
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();
2022-07-14 22:19:01 +02:00
//filePublishMenuItem.Enabled = SteamworksUGC.IsInit;
2020-09-11 23:46:04 +03:00
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();
2020-09-14 13:40:09 +03:00
ClearAllTools();
2020-09-11 23:46:04 +03:00
RefreshAvailableTools();
RefreshActiveTool();
}
}
private void fileOpenMenuItem_Click(object sender, EventArgs e)
{
if (!PromptSaveMap())
{
return;
}
#if DEVELOPER
2022-07-13 01:09:46 +02:00
var pgmFilter = "|PGM files (*.pgm)|*.pgm";
string allSupported = "All supported types (*.ini;*.bin;*.mpr;*.pgm)|*.ini;*.bin;*.mpr;*.pmg";
2020-09-11 23:46:04 +03:00
#else
2022-07-13 01:09:46 +02:00
var pgmFilter = string.Empty;
string allSupported = "All supported types (*.ini;*.bin;*.mpr)|*.ini;*.bin;*.mpr";
2020-09-11 23:46:04 +03:00
#endif
OpenFileDialog ofd = new OpenFileDialog
{
AutoUpgradeEnabled = false,
RestoreDirectory = true
};
2022-07-13 01:09:46 +02:00
ofd.Filter = allSupported + "|Tiberian Dawn files (*.ini;*.bin)|*.ini;*.bin|Red Alert files (*.mpr)|*.mpr" + pgmFilter + "|All files (*.*)|*.*";
2020-09-11 23:46:04 +03:00
if (plugin != null)
{
switch (plugin.GameType)
{
case GameType.TiberianDawn:
ofd.InitialDirectory = Path.GetDirectoryName(filename) ?? TiberianDawn.Constants.SaveDirectory;
2022-07-13 01:09:46 +02:00
ofd.FilterIndex = 2;
2020-09-11 23:46:04 +03:00
break;
case GameType.RedAlert:
ofd.InitialDirectory = Path.GetDirectoryName(filename) ?? RedAlert.Constants.SaveDirectory;
2022-07-13 01:09:46 +02:00
ofd.FilterIndex = 3;
2020-09-11 23:46:04 +03:00
break;
}
}
else
{
ofd.InitialDirectory = Globals.RootSaveDirectory;
}
if (ofd.ShowDialog() == DialogResult.OK)
{
OpenFile(ofd.FileName, false);
2020-09-11 23:46:04 +03:00
}
}
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:
2022-07-14 22:19:01 +02:00
filters.Add("Red Alert files (*.mpr;*.ini)|*.mpr;*.ini");
2020-09-11 23:46:04 +03:00
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)
{
OpenFile(e.FullName, true);
2020-09-11 23:46:04 +03:00
}
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.Buildings[location] as Building;
2020-09-11 23:46:04 +03:00
var buildingType = building?.Type;
if (buildingType != null)
{
sb.AppendFormat(", Building = {0}", buildingType.DisplayName);
}
cellStatusLabel.Text = sb.ToString();
}
else
{
cellStatusLabel.Text = "No cell";
2020-09-11 23:46:04 +03:00
}
}
}
private void OpenFile(String fileName, bool askSave)
{
if (askSave && !PromptSaveMap())
{
return;
}
var fileInfo = new FileInfo(fileName);
if (LoadFile(fileInfo.FullName))
{
mru.Add(fileInfo);
}
else
{
mru.Remove(fileInfo);
MessageBox.Show(string.Format("Error loading {0}.", fileName), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
2020-09-11 23:46:04 +03:00
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();
2020-09-14 13:40:09 +03:00
ClearAllTools();
2020-09-11 23:46:04 +03:00
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;
}
var fileInfo = new FileInfo(saveFilename);
if (fileInfo.Exists && fileInfo.Length > Globals.MaxMapSize)
2020-09-11 23:46:04 +03:00
{
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;
}
foreach (var toolStripButton in viewToolStripButtons)
{
toolStripButton.Enabled = (availableToolTypes & toolStripButton.ToolType) != ToolType.None;
}
2020-09-11 23:46:04 +03:00
ActiveToolType = activeToolType;
}
2020-09-14 13:40:09 +03:00
private void ClearAllTools()
{
ClearActiveTool();
foreach (var kvp in toolForms)
{
kvp.Value.Dispose();
}
toolForms.Clear();
}
2020-09-11 23:46:04 +03:00
private void ClearActiveTool()
{
2020-09-14 13:40:09 +03:00
activeTool?.Deactivate();
2020-09-11 23:46:04 +03:00
activeTool = null;
if (activeToolForm != null)
{
activeToolForm.ResizeEnd -= ActiveToolForm_ResizeEnd;
2020-09-14 13:40:09 +03:00
activeToolForm.Hide();
2020-09-11 23:46:04 +03:00
activeToolForm = null;
}
toolStatusLabel.Text = string.Empty;
}
private void RefreshActiveTool()
{
if (plugin == null)
{
return;
}
if (activeTool == null)
{
activeLayers = MapLayerFlag.None;
}
ClearActiveTool();
2020-09-14 13:40:09 +03:00
bool found = toolForms.TryGetValue(ActiveToolType, out IToolDialog toolDialog);
2020-09-11 23:46:04 +03:00
2020-09-14 13:40:09 +03:00
if (!found)
{
switch (ActiveToolType)
{
case ToolType.Map:
2020-09-11 23:46:04 +03:00
{
toolDialog = new TemplateToolDialog(this);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Smudge:
2020-09-11 23:46:04 +03:00
{
toolDialog = new SmudgeToolDialog(this);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Overlay:
2020-09-11 23:46:04 +03:00
{
toolDialog = new OverlayToolDialog(this);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Resources:
2020-09-11 23:46:04 +03:00
{
toolDialog = new ResourcesToolDialog(this);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Terrain:
2020-09-11 23:46:04 +03:00
{
toolDialog = new TerrainToolDialog(this, plugin);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Infantry:
2020-09-11 23:46:04 +03:00
{
toolDialog = new InfantryToolDialog(this, plugin);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Unit:
{
toolDialog = new UnitToolDialog(this, plugin);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Building:
{
toolDialog = new BuildingToolDialog(this, plugin);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Wall:
{
toolDialog = new WallsToolDialog(this);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.Waypoint:
{
toolDialog = new WaypointsToolDialog(this);
2020-09-14 13:40:09 +03:00
}
break;
case ToolType.CellTrigger:
{
toolDialog = new CellTriggersToolDialog(this);
2020-09-14 13:40:09 +03:00
}
break;
}
2020-09-11 23:46:04 +03:00
2020-09-14 13:40:09 +03:00
if (toolDialog != null)
{
toolForms.Add(ActiveToolType, toolDialog);
}
2020-09-11 23:46:04 +03:00
}
2020-09-14 13:40:09 +03:00
if (toolDialog != null)
2020-09-11 23:46:04 +03:00
{
2020-09-14 13:40:09 +03:00
activeToolForm = (Form)toolDialog;
toolDialog.Initialize(mapPanel, ActiveLayers, toolStatusLabel, mouseToolTip, plugin, url);
2020-09-14 13:40:09 +03:00
activeTool = toolDialog.GetTool();
activeToolForm.ResizeEnd -= ActiveToolForm_ResizeEnd;
activeToolForm.Shown -= this.ActiveToolForm_Shown;
activeToolForm.Shown += this.ActiveToolForm_Shown;
2020-09-14 13:40:09 +03:00
activeToolForm.Show(this);
activeTool.Activate();
2020-09-11 23:46:04 +03:00
activeToolForm.ResizeEnd += ActiveToolForm_ResizeEnd;
}
switch (plugin.GameType)
{
case GameType.TiberianDawn:
mapPanel.MaxZoom = 8;
2020-09-12 17:31:00 +03:00
mapPanel.ZoomStep = 0.15;
2020-09-11 23:46:04 +03:00
break;
case GameType.RedAlert:
mapPanel.MaxZoom = 16;
2020-09-12 17:31:00 +03:00
mapPanel.ZoomStep = 0.2;
2020-09-11 23:46:04 +03:00
break;
}
// Refresh toolstrip button checked states
foreach (var toolStripButton in viewToolStripButtons)
{
toolStripButton.Checked = ActiveToolType == toolStripButton.ToolType;
}
2020-09-11 23:46:04 +03:00
Focus();
UpdateVisibleLayers();
mapPanel.Invalidate();
}
private void ClampActiveToolForm()
2020-09-11 23:46:04 +03:00
{
ClampForm(activeToolForm);
}
public static void ClampForm(Form toolform)
{
if (toolform == null)
2020-09-11 23:46:04 +03:00
{
return;
}
Rectangle bounds = toolform.DesktopBounds;
Rectangle workingArea = Screen.FromControl(toolform).WorkingArea;
2020-09-11 23:46:04 +03:00
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;
}
toolform.DesktopBounds = bounds;
2020-09-11 23:46:04 +03:00
}
private void ActiveToolForm_ResizeEnd(object sender, EventArgs e)
{
ClampActiveToolForm();
}
private void ActiveToolForm_Shown(object sender, EventArgs e)
{
Form tool = sender as Form;
if (tool != null)
{
ClampForm(tool);
}
2020-09-11 23:46:04 +03:00
}
private void Triggers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
RefreshAvailableTools();
}
private void mainToolStripButton_Click(object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
ActiveToolType = ((ViewToolStripButton)sender).ToolType;
2020-09-11 23:46:04 +03:00
}
private void MapPanel_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
String[] files = (String[])e.Data.GetData(DataFormats.FileDrop);
if (files.Length == 1)
e.Effect = DragDropEffects.Copy;
}
}
private void MapPanel_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
String[] files = (String[])e.Data.GetData(DataFormats.FileDrop);
if (files.Length != 1)
return;
OpenFile(files[0], true);
}
2020-09-11 23:46:04 +03:00
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
{
2022-07-14 22:19:01 +02:00
System.Diagnostics.Process.Start(path);
2020-09-11 23:46:04 +03:00
}
2022-07-14 22:19:01 +02:00
catch (System.ComponentModel.Win32Exception)
2020-09-11 23:46:04 +03:00
{
2022-07-14 22:19:01 +02:00
System.Diagnostics.Process.Start("notepad.exe", path);
2020-09-11 23:46:04 +03:00
}
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;
}
2022-07-14 22:19:01 +02:00
if (!SteamworksUGC.IsInit)
{
MessageBox.Show("Steam interface is not initialized. To enable Workshop publishing, log into Steam and restart the editor.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
2020-09-11 23:46:04 +03:00
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_Shown(object sender, System.EventArgs e)
{
if (filename != null)
this.OpenFile(filename, false);
}
2020-09-11 23:46:04 +03:00
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;
}
}
}