1867 lines
73 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;
2022-09-01 13:05:49 +02:00
using MobiusEditor.Tools;
2020-09-11 23:46:04 +03:00
using MobiusEditor.Tools.Dialogs;
using MobiusEditor.Utility;
using Steamworks;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
2022-09-01 13:05:49 +02:00
using System.Drawing.Imaging;
2020-09-11 23:46:04 +03:00
using System.IO;
using System.Linq;
2022-08-08 11:53:51 +02:00
using System.Reflection;
2020-09-11 23:46:04 +03:00
using System.Text;
using System.Windows.Forms;
namespace MobiusEditor
{
public partial class MainForm : Form, IFeedBackHandler
2020-09-11 23:46:04 +03:00
{
2022-09-05 16:22:14 +02:00
public Dictionary<GameType, string[]> ModPaths { get; set; }
2022-09-01 13:05:49 +02:00
private Dictionary<int, Bitmap> theaterIcons = new Dictionary<int, Bitmap>();
2020-09-11 23:46:04 +03:00
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)
{
2022-09-01 13:05:49 +02:00
MapLayerFlag active = activeLayers;
2022-09-25 12:11:59 +02:00
// Save some processing by just always removing these.
if (plugin.GameType != GameType.SoleSurvivor)
{
active &= ~MapLayerFlag.FootballArea;
}
else if (plugin.GameType != GameType.RedAlert)
2022-09-01 13:05:49 +02:00
{
active &= ~MapLayerFlag.BuildingFakes;
}
activeTool.Layers = active;
2020-09-11 23:46:04 +03:00
}
}
}
}
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 FileType loadedFileType;
2020-09-11 23:46:04 +03:00
private string filename;
private readonly MRU mru;
private readonly UndoRedoList<UndoRedoEventArgs> url = new UndoRedoList<UndoRedoEventArgs>(Globals.UndoRedoStackSize);
2020-09-11 23:46:04 +03:00
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();
// Obey the setting.
this.mapPanel.SmoothScale = Globals.MapSmoothScale;
2022-08-08 11:53:51 +02:00
SetTitle();
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,
2022-09-19 12:23:44 +02:00
cellTriggersToolStripButton,
selectToolStripButton,
};
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;
}
2022-08-08 11:53:51 +02:00
private void SetTitle()
{
2022-09-19 12:23:44 +02:00
const string noname = "Untitled";
String mainTitle = GetProgramVersionTitle();
2022-09-25 12:11:59 +02:00
if (plugin == null)
2022-08-08 11:53:51 +02:00
{
2022-09-25 12:11:59 +02:00
this.Text = mainTitle;
return;
2022-08-08 11:53:51 +02:00
}
2022-09-25 12:11:59 +02:00
string mapName = plugin.Map.BasicSection.Name;
if (String.IsNullOrEmpty(mapName))
2022-08-08 11:53:51 +02:00
{
2022-09-25 12:11:59 +02:00
if (filename != null)
{
mapName = Path.GetFileName(filename);
}
else
{
mapName = noname;
}
2022-08-08 11:53:51 +02:00
}
2022-09-25 12:11:59 +02:00
this.Text = string.Format("{0} [{1}] - {2}{3}", mainTitle, plugin.Name, mapName, plugin != null && plugin.Dirty ? " *" : String.Empty);
2022-08-08 11:53:51 +02:00
}
private String GetProgramVersionTitle()
{
AssemblyName assn = Assembly.GetExecutingAssembly().GetName();
System.Version currentVersion = assn.Version;
2022-09-25 12:11:59 +02:00
return string.Format("Mobius Editor v{0}", currentVersion);
}
2020-09-11 23:46:04 +03:00
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)
{
2022-09-19 12:23:44 +02:00
OemScanCode sc = Keyboard.GetScanCode(msg);
if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.None)
2020-09-11 23:46:04 +03:00
{
2022-09-19 12:23:44 +02:00
switch (sc)
{
case OemScanCode.Q:
mapToolStripButton.PerformClick();
return true;
case OemScanCode.W:
smudgeToolStripButton.PerformClick();
return true;
case OemScanCode.E:
overlayToolStripButton.PerformClick();
return true;
case OemScanCode.R:
terrainToolStripButton.PerformClick();
return true;
case OemScanCode.T:
infantryToolStripButton.PerformClick();
return true;
case OemScanCode.Y:
unitToolStripButton.PerformClick();
return true;
case OemScanCode.A:
buildingToolStripButton.PerformClick();
return true;
case OemScanCode.S:
resourcesToolStripButton.PerformClick();
return true;
case OemScanCode.D:
wallsToolStripButton.PerformClick();
return true;
case OemScanCode.F:
waypointsToolStripButton.PerformClick();
return true;
case OemScanCode.G:
cellTriggersToolStripButton.PerformClick();
return true;
case OemScanCode.H:
selectToolStripButton.PerformClick();
return true;
}
2020-09-11 23:46:04 +03:00
}
else if (keyData == (Keys.Control | Keys.Z))
{
if (editUndoMenuItem.Enabled)
{
EditUndoMenuItem_Click(this, new EventArgs());
2020-09-11 23:46:04 +03:00
}
return true;
}
else if (keyData == (Keys.Control | Keys.Y))
{
if (editRedoMenuItem.Enabled)
{
EditRedoMenuItem_Click(this, new EventArgs());
2020-09-11 23:46:04 +03:00
}
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void UpdateUndoRedo()
{
editUndoMenuItem.Enabled = url.CanUndo;
editRedoMenuItem.Enabled = url.CanRedo;
editClearUndoRedoMenuItem.Enabled = url.CanUndo || url.CanRedo;
2020-09-11 23:46:04 +03:00
}
private void UndoRedo_Updated(object sender, EventArgs e)
{
UpdateUndoRedo();
}
private void FileNewMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (!PromptSaveMap())
{
return;
}
2022-09-01 13:05:49 +02:00
using (NewMapDialog nmd = new NewMapDialog())
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
if (nmd.ShowDialog() != DialogResult.OK)
{
return;
}
2020-09-11 23:46:04 +03:00
if (plugin != null)
{
plugin.Dispose();
}
plugin = null;
2022-09-05 16:22:14 +02:00
string[] modPaths = null;
if (ModPaths != null)
{
ModPaths.TryGetValue(nmd.GameType, out modPaths);
2022-09-05 16:22:14 +02:00
}
Globals.TheTextureManager.ExpandModPaths = modPaths;
2020-09-11 23:46:04 +03:00
Globals.TheTextureManager.Reset();
2022-09-05 16:22:14 +02:00
Globals.TheTilesetManager.ExpandModPaths = modPaths;
Globals.TheTilesetManager.Reset();
Globals.TheTeamColorManager.ExpandModPaths = modPaths;
2020-09-11 23:46:04 +03:00
if (nmd.GameType == GameType.TiberianDawn)
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
2022-09-20 00:08:07 +02:00
plugin = new TiberianDawn.GamePlugin(nmd.MegaMap, this);
2020-09-11 23:46:04 +03:00
plugin.New(nmd.TheaterName);
}
else if (nmd.GameType == GameType.RedAlert)
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
plugin = new RedAlert.GamePlugin(this);
2020-09-11 23:46:04 +03:00
plugin.New(nmd.TheaterName);
}
2022-09-22 01:26:46 +02:00
else if (nmd.GameType == GameType.SoleSurvivor)
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
plugin = new SoleSurvivor.GamePlugin(this);
plugin.New(nmd.TheaterName);
}
2020-09-11 23:46:04 +03:00
if (SteamworksUGC.IsInit)
{
plugin.Map.BasicSection.Author = SteamFriends.GetPersonaName();
}
2022-09-01 13:05:49 +02:00
LoadIcons(plugin);
2020-09-11 23:46:04 +03:00
mapPanel.MapImage = plugin.MapImage;
filename = null;
2022-08-08 11:53:51 +02:00
SetTitle();
2020-09-11 23:46:04 +03:00
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)
2020-09-11 23:46:04 +03:00
{
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
2022-09-01 13:05:49 +02:00
String selectedFileName = null;
using (OpenFileDialog ofd = new OpenFileDialog())
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
ofd.AutoUpgradeEnabled = false;
ofd.RestoreDirectory = true;
ofd.Filter = allSupported + "|Tiberian Dawn files (*.ini;*.bin)|*.ini;*.bin|Red Alert files (*.mpr;*.ini)|*.mpr;*.ini" + pgmFilter + "|All files (*.*)|*.*";
if (plugin != null)
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
switch (plugin.GameType)
{
case GameType.TiberianDawn:
2022-09-22 01:26:46 +02:00
case GameType.SoleSurvivor:
2022-09-01 13:05:49 +02:00
ofd.InitialDirectory = Path.GetDirectoryName(filename) ?? TiberianDawn.Constants.SaveDirectory;
//ofd.FilterIndex = 2;
break;
case GameType.RedAlert:
ofd.InitialDirectory = Path.GetDirectoryName(filename) ?? RedAlert.Constants.SaveDirectory;
//ofd.FilterIndex = 3;
break;
}
}
else
{
ofd.InitialDirectory = Globals.RootSaveDirectory;
}
if (ofd.ShowDialog() == DialogResult.OK)
{
selectedFileName = ofd.FileName;
2020-09-11 23:46:04 +03:00
}
}
2022-09-01 13:05:49 +02:00
if (selectedFileName != null)
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
OpenFile(selectedFileName, false);
2020-09-11 23:46:04 +03:00
}
}
private void FileSaveMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (plugin == null)
{
return;
}
if (string.IsNullOrEmpty(filename) || !Directory.Exists(Path.GetDirectoryName(filename)))
2020-09-11 23:46:04 +03:00
{
fileSaveAsMenuItem.PerformClick();
}
else
{
String errors = plugin.Validate();
if (errors != null)
{
2022-09-01 13:05:49 +02:00
MessageBox.Show(errors, "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
return;
}
2020-09-11 23:46:04 +03:00
var fileInfo = new FileInfo(filename);
if (SaveFile(fileInfo.FullName, loadedFileType))
2020-09-11 23:46:04 +03:00
{
mru.Add(fileInfo);
}
else
{
mru.Remove(fileInfo);
}
}
}
private void FileSaveAsMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (plugin == null)
{
return;
}
String errors = plugin.Validate();
if (errors != null)
{
MessageBox.Show(errors, "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
2022-09-01 13:05:49 +02:00
string savePath = null;
using (SaveFileDialog sfd = new SaveFileDialog())
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
sfd.AutoUpgradeEnabled = false;
sfd.RestoreDirectory = true;
var filters = new List<string>();
switch (plugin.GameType)
{
case GameType.TiberianDawn:
2022-09-22 01:26:46 +02:00
case GameType.SoleSurvivor:
2022-09-01 13:05:49 +02:00
filters.Add("Tiberian Dawn files (*.ini;*.bin)|*.ini;*.bin");
sfd.InitialDirectory = TiberianDawn.Constants.SaveDirectory;
break;
case GameType.RedAlert:
filters.Add("Red Alert files (*.mpr;*.ini)|*.mpr;*.ini");
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(this) == DialogResult.OK)
{
savePath = sfd.FileName;
}
2020-09-11 23:46:04 +03:00
}
2022-09-01 13:05:49 +02:00
if (savePath != null)
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
var fileInfo = new FileInfo(savePath);
if (SaveFile(fileInfo.FullName, FileType.INI))
2020-09-11 23:46:04 +03:00
{
mru.Add(fileInfo);
}
else
{
mru.Remove(fileInfo);
}
}
}
private void FileExportMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (plugin == null)
{
return;
}
String errors = plugin.Validate();
if (errors != null)
{
MessageBox.Show(errors, "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
2022-09-01 13:05:49 +02:00
string savePath = null;
using (SaveFileDialog sfd = new SaveFileDialog())
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
sfd.AutoUpgradeEnabled = false;
sfd.RestoreDirectory = true;
sfd.Filter = "MEG files (*.meg)|*.meg";
if (sfd.ShowDialog(this) == DialogResult.OK)
{
savePath = sfd.FileName;
}
}
if (savePath != null)
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
plugin.Save(savePath, FileType.MEG);
2020-09-11 23:46:04 +03:00
}
}
private void FileExitMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
Close();
}
private void EditUndoMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (url.CanUndo)
{
2022-09-19 12:23:44 +02:00
url.Undo(new UndoRedoEventArgs(mapPanel, plugin));
2020-09-11 23:46:04 +03:00
}
}
private void EditRedoMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (url.CanRedo)
{
2022-09-19 12:23:44 +02:00
url.Redo(new UndoRedoEventArgs(mapPanel, plugin));
2020-09-11 23:46:04 +03:00
}
}
private void EditClearUndoRedoMenuItem_Click(object sender, EventArgs e)
{
if (DialogResult.Yes == MessageBox.Show("This will remove all undo/redo information. Are you sure?", GetProgramVersionTitle(), MessageBoxButtons.YesNo))
{
url.Clear();
}
}
private void SettingsMapSettingsMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (plugin == null)
{
return;
}
bool expansionEnabled = plugin.Map.BasicSection.ExpansionEnabled;
bool rulesChanged = false;
2022-09-19 12:23:44 +02:00
PropertyTracker<BasicSection> basicSettings = new PropertyTracker<BasicSection>(plugin.Map.BasicSection);
PropertyTracker<BriefingSection> briefingSettings = new PropertyTracker<BriefingSection>(plugin.Map.BriefingSection);
2022-09-22 01:26:46 +02:00
PropertyTracker<SoleSurvivor.CratesSection> cratesSettings = null;
if (plugin is SoleSurvivor.GamePlugin ssPlugin)
{
cratesSettings = new PropertyTracker<SoleSurvivor.CratesSection>(ssPlugin.CratesSection);
}
string extraIniText = (plugin.GameType == GameType.RedAlert ? plugin.ExtraIniText : String.Empty).Trim();
2022-09-19 12:23:44 +02:00
Dictionary<House, PropertyTracker<House>> houseSettingsTrackers = plugin.Map.Houses.ToDictionary(h => h, h => new PropertyTracker<House>(h));
2022-09-22 01:26:46 +02:00
using (MapSettingsDialog msd = new MapSettingsDialog(plugin, basicSettings, briefingSettings, cratesSettings, houseSettingsTrackers, extraIniText))
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
msd.StartPosition = FormStartPosition.CenterParent;
if (msd.ShowDialog(this) == DialogResult.OK)
2020-09-11 23:46:04 +03:00
{
2022-09-19 12:23:44 +02:00
bool hasChanges = basicSettings.HasChanges || briefingSettings.HasChanges;
2022-09-01 13:05:49 +02:00
basicSettings.Commit();
briefingSettings.Commit();
2022-09-22 01:26:46 +02:00
if (cratesSettings != null)
{
cratesSettings.Commit();
}
2022-09-01 13:05:49 +02:00
foreach (var houseSettingsTracker in houseSettingsTrackers.Values)
{
2022-09-19 12:23:44 +02:00
if (houseSettingsTracker.HasChanges)
hasChanges = true;
2022-09-01 13:05:49 +02:00
houseSettingsTracker.Commit();
}
if (plugin.GameType == GameType.RedAlert && !extraIniText.Equals(msd.ExtraIniText, StringComparison.InvariantCultureIgnoreCase))
{
plugin.ExtraIniText = msd.ExtraIniText;
rulesChanged = true;
2022-09-19 12:23:44 +02:00
hasChanges = true;
}
2022-09-19 12:23:44 +02:00
plugin.Dirty = hasChanges;
2020-09-11 23:46:04 +03:00
}
}
if (rulesChanged || (expansionEnabled && !plugin.Map.BasicSection.ExpansionEnabled))
{
// If Aftermath units were disabled, we can't guarantee none of them are still in
// the undo/redo history, so the undo/redo history is cleared to avoid issues.
// The rest of the cleanup can be found in the ViewTool class, in the BasicSection_PropertyChanged function.
// Rule changes will clear undo to avoid conflicts with placed smudge types.
url.Clear();
}
2020-09-11 23:46:04 +03:00
}
private void SettingsTeamTypesMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (plugin == null)
{
return;
}
int maxTeams = 0;
switch (plugin.GameType)
{
case GameType.TiberianDawn:
2022-09-22 01:26:46 +02:00
case GameType.SoleSurvivor:
maxTeams = TiberianDawn.Constants.MaxTeams;
2020-09-11 23:46:04 +03:00
break;
case GameType.RedAlert:
2022-09-22 01:26:46 +02:00
maxTeams = RedAlert.Constants.MaxTeams;
2020-09-11 23:46:04 +03:00
break;
}
2022-09-01 13:05:49 +02:00
using (TeamTypesDialog ttd = new TeamTypesDialog(plugin, maxTeams))
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
ttd.StartPosition = FormStartPosition.CenterParent;
if (ttd.ShowDialog(this) == DialogResult.OK)
{
plugin.Map.TeamTypes.Clear();
plugin.Map.TeamTypes.AddRange(ttd.TeamTypes.OrderBy(t => t.Name, new ExplorerComparer()).Select(t => t.Clone()));
plugin.Dirty = true;
}
2020-09-11 23:46:04 +03:00
}
}
private void SettingsTriggersMenuItem_Click(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
if (plugin == null)
{
return;
}
int maxTriggers = 0;
switch (plugin.GameType)
{
case GameType.TiberianDawn:
2022-09-22 01:26:46 +02:00
case GameType.SoleSurvivor:
maxTriggers = TiberianDawn.Constants.MaxTriggers;
2020-09-11 23:46:04 +03:00
break;
case GameType.RedAlert:
2022-09-22 01:26:46 +02:00
maxTriggers = RedAlert.Constants.MaxTriggers;
2020-09-11 23:46:04 +03:00
break;
}
2022-09-01 13:05:49 +02:00
using (TriggersDialog td = new TriggersDialog(plugin, maxTriggers))
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
td.StartPosition = FormStartPosition.CenterParent;
if (td.ShowDialog(this) == DialogResult.OK)
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
List<Trigger> reordered = td.Triggers.OrderBy(t => t.Name, new ExplorerComparer()).ToList();
if (Trigger.CheckForChanges(plugin.Map.Triggers.ToList(), reordered))
{
plugin.Dirty = true;
2022-09-05 16:22:14 +02:00
plugin.Map.Triggers = reordered;
2022-09-01 13:05:49 +02:00
RefreshAvailableTools();
}
}
}
}
private void ToolsPowerMenuItem_Click(Object sender, EventArgs e)
2022-09-01 13:05:49 +02:00
{
if (plugin == null)
{
return;
}
using (ErrorMessageBox emb = new ErrorMessageBox())
{
emb.Title = "Power usage";
emb.Message = "Power balance per House:";
emb.Errors = plugin.Map.AssessPower(plugin.GameType);
emb.StartPosition = FormStartPosition.CenterParent;
emb.ShowDialog(this);
}
}
private void ToolsStorageMenuItem_Click(Object sender, EventArgs e)
{
if (plugin == null)
{
return;
}
using (ErrorMessageBox emb = new ErrorMessageBox())
{
emb.Title = "Silo storage";
emb.Message = "Available silo storage per House:";
emb.Errors = plugin.Map.AssessStorage(plugin.GameType);
emb.StartPosition = FormStartPosition.CenterParent;
emb.ShowDialog(this);
}
}
2022-09-19 12:23:44 +02:00
private void ToolsRandomizeTilesMenuItem_Click(Object sender, EventArgs e)
{
if (plugin != null)
{
String feedback = TemplateTool.RandomizeTiles(plugin, mapPanel, url);
MessageBox.Show(feedback, GetProgramVersionTitle());
}
}
private void ToolsExportImage_Click(Object sender, EventArgs e)
2022-09-01 13:05:49 +02:00
{
if (plugin == null)
{
return;
}
using (ImageExportDialog imex = new ImageExportDialog(plugin, activeLayers, filename))
2022-09-01 13:05:49 +02:00
{
imex.ShowDialog(this);
2020-09-11 23:46:04 +03:00
}
}
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)
2020-09-11 23:46:04 +03:00
{
if (plugin != null)
{
var mapPoint = mapPanel.ClientToMap(e.Location);
var location = new Point((int)Math.Floor((double)mapPoint.X / Globals.MapTileWidth), (int)Math.Floor((double)mapPoint.Y / Globals.MapTileHeight));
2020-09-11 23:46:04 +03:00
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.MapTileWidth) % Globals.PixelWidth,
(mapPoint.Y * Globals.PixelHeight / Globals.MapTileHeight) % Globals.PixelHeight
2020-09-11 23:46:04 +03:00
);
2022-09-19 12:23:44 +02:00
InfantryStoppingType i = InfantryGroup.ClosestStoppingTypes(subPixel).First();
Infantry inf = infantryGroup.Infantry[(int)i];
if (inf != null)
2020-09-11 23:46:04 +03:00
{
2022-09-19 12:23:44 +02:00
sb.AppendFormat(", Infantry = {0} ({1})", inf.Type.DisplayName, InfantryGroup.GetStoppingTypeName(i));
2020-09-11 23:46:04 +03:00
}
}
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)
{
try
{
if (!File.Exists(loadFilename))
{
return false;
}
}
catch
{
return false;
}
2020-09-11 23:46:04 +03:00
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)
{
long filesize = 0;
try
{
filesize = new FileInfo(loadFilename).Length;
var bytes = File.ReadAllBytes(loadFilename);
var enc = new UTF8Encoding(false, true);
var inicontents = enc.GetString(bytes);
var ini = new INI();
ini.Parse(inicontents);
// if it gets to this point, the file is a text document.
fileType = FileType.INI;
}
catch
{
Size tdMax = TiberianDawn.Constants.MaxSize;
if (filesize == tdMax.Width * tdMax.Height * 2)
{
fileType = FileType.BIN;
}
else
{
return false;
}
}
2020-09-11 23:46:04 +03:00
}
GameType gameType = GameType.None;
switch (fileType)
{
case FileType.INI:
2022-09-20 18:40:27 +02:00
{
gameType = RedAlert.GamePlugin.CheckForRAMap(loadFilename, fileType) ? GameType.RedAlert : GameType.TiberianDawn;
2022-09-20 18:40:27 +02:00
break;
}
2020-09-11 23:46:04 +03:00
case FileType.BIN:
2022-09-20 18:40:27 +02:00
{
gameType = File.Exists(Path.ChangeExtension(loadFilename, ".ini")) ? GameType.TiberianDawn : GameType.None;
break;
}
2020-09-11 23:46:04 +03:00
#if DEVELOPER
case FileType.PGM:
{
try
2020-09-11 23:46:04 +03:00
{
using (var megafile = new Megafile(loadFilename))
2020-09-11 23:46:04 +03:00
{
if (megafile.Any(f => Path.GetExtension(f).ToLower() == ".mpr"))
2020-09-11 23:46:04 +03:00
{
gameType = GameType.RedAlert;
}
else
{
gameType = GameType.TiberianDawn;
2020-09-11 23:46:04 +03:00
}
}
}
catch (FileNotFoundException)
{
return false;
}
2020-09-11 23:46:04 +03:00
break;
}
2020-09-11 23:46:04 +03:00
#endif
}
bool isTdMegaMap = false;
2022-09-20 18:40:27 +02:00
if (gameType == GameType.TiberianDawn)
{
isTdMegaMap = TiberianDawn.GamePlugin.CheckForMegamap(loadFilename, fileType);
if (isTdMegaMap && SoleSurvivor.GamePlugin.CheckForSSmap(loadFilename, fileType))
2022-09-20 18:40:27 +02:00
{
gameType = GameType.SoleSurvivor;
}
}
2020-09-11 23:46:04 +03:00
if (gameType == GameType.None)
{
return false;
}
2022-09-01 13:05:49 +02:00
Unload();
2020-09-11 23:46:04 +03:00
if (plugin != null)
{
plugin.Dispose();
}
plugin = null;
2022-09-05 16:22:14 +02:00
string[] modPaths = null;
if (ModPaths != null)
{
ModPaths.TryGetValue(gameType, out modPaths);
2022-09-05 16:22:14 +02:00
}
Globals.TheTextureManager.ExpandModPaths = modPaths;
2020-09-11 23:46:04 +03:00
Globals.TheTextureManager.Reset();
2022-09-05 16:22:14 +02:00
Globals.TheTilesetManager.ExpandModPaths = modPaths;
Globals.TheTilesetManager.Reset();
Globals.TheTeamColorManager.ExpandModPaths = modPaths;
2020-09-11 23:46:04 +03:00
switch (gameType)
{
case GameType.TiberianDawn:
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
plugin = new TiberianDawn.GamePlugin(isTdMegaMap, this);
2020-09-11 23:46:04 +03:00
break;
case GameType.RedAlert:
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
plugin = new RedAlert.GamePlugin(this);
2020-09-11 23:46:04 +03:00
break;
2022-09-20 18:40:27 +02:00
case GameType.SoleSurvivor:
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
2022-09-22 01:26:46 +02:00
plugin = new SoleSurvivor.GamePlugin(this);
2022-09-20 18:40:27 +02:00
break;
2020-09-11 23:46:04 +03:00
}
string[] errors;
2022-09-19 12:23:44 +02:00
bool modifiedByLoad;
2020-09-11 23:46:04 +03:00
try
{
2022-09-19 12:23:44 +02:00
// Simply being flagged as single play no longer flags maps as "modified by the loading process",
// to make it simpler to open classic maps and examine them without getting a save prompt on close.
errors = plugin.Load(loadFilename, fileType, out modifiedByLoad).ToArray();
2022-09-01 13:05:49 +02:00
LoadIcons(plugin);
2020-09-11 23:46:04 +03:00
if (errors.Length > 0)
{
using (ErrorMessageBox emb = new ErrorMessageBox())
{
emb.Errors = errors;
2022-09-01 13:05:49 +02:00
emb.StartPosition = FormStartPosition.CenterParent;
emb.ShowDialog(this);
}
2020-09-11 23:46:04 +03:00
}
}
catch (Exception ex)
2020-09-11 23:46:04 +03:00
{
2022-09-25 12:11:59 +02:00
String errorMessage = "Error loading map: " + ex.Message;
#if DEBUG
2022-09-25 12:11:59 +02:00
errorMessage += "\n\n" + ex.StackTrace;
#endif
2022-09-25 12:11:59 +02:00
MessageBox.Show(errorMessage, this.GetProgramVersionTitle(), MessageBoxButtons.OK, MessageBoxIcon.Error);
2022-09-01 13:05:49 +02:00
this.Unload();
2020-09-11 23:46:04 +03:00
#if DEVELOPER
throw;
#else
return false;
#endif
}
mapPanel.MapImage = plugin.MapImage;
filename = loadFilename;
loadedFileType = fileType;
2022-09-19 12:23:44 +02:00
plugin.Dirty = modifiedByLoad;
2020-09-11 23:46:04 +03:00
url.Clear();
2020-09-14 13:40:09 +03:00
ClearAllTools();
2020-09-11 23:46:04 +03:00
RefreshAvailableTools();
RefreshActiveTool();
2022-09-19 12:23:44 +02:00
SetTitle();
2020-09-11 23:46:04 +03:00
return true;
}
2022-09-01 13:05:49 +02:00
/// <summary>
/// This clears the UI and plugin in a safe way, ending up with a blank slate.
/// </summary>
private void Unload()
{
try
{
url.Clear();
// Disable all tools
ActiveToolType = ToolType.None;
this.ActiveControl = null;
ClearAllTools();
// Unlink plugin
IGamePlugin pl = plugin;
plugin = null;
// Remove tools
RefreshAvailableTools();
// Clear UI
mapPanel.MapImage = null;
mapPanel.Invalidate();
// Dispose plugin
if (pl != null)
{
pl.Dispose();
}
// Unload graphics
Globals.TheTilesetManager.Reset();
Globals.TheTextureManager.Reset();
// Clean up loaded file status
filename = null;
loadedFileType = FileType.None;
SetTitle();
}
catch
{
// Ignore.
}
}
private bool SaveFile(string saveFilename, FileType inputNameType)
2020-09-11 23:46:04 +03:00
{
// This part assumes validation is already done.
2020-09-11 23:46:04 +03:00
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)
{
if (inputNameType == FileType.None)
{
return false;
}
else
{
fileType = inputNameType;
}
2020-09-11 23:46:04 +03:00
}
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;
2022-08-08 11:53:51 +02:00
SetTitle();
2020-09-11 23:46:04 +03:00
return true;
}
private void RefreshAvailableTools()
{
2022-09-01 13:05:49 +02:00
// Menu items
if (plugin != null)
{
fileSaveMenuItem.Enabled = true;
fileSaveAsMenuItem.Enabled = true;
filePublishMenuItem.Enabled = true;
fileExportMenuItem.Enabled = true;
editUndoMenuItem.Enabled = url.CanUndo;
editRedoMenuItem.Enabled = url.CanRedo;
editClearUndoRedoMenuItem.Enabled = url.CanUndo || url.CanRedo;
2022-09-01 13:05:49 +02:00
settingsMapSettingsMenuItem.Enabled = true;
settingsTeamTypesMenuItem.Enabled = true;
settingsTriggersMenuItem.Enabled = true;
toolsPowerMenuItem.Enabled = true;
toolsExportImageMenuItem.Enabled = true;
developerGoToINIMenuItem.Enabled = true;
developerDebugShowOverlapCellsMenuItem.Enabled = true;
developerGenerateMapPreviewDirectoryMenuItem.Enabled = true;
2022-09-25 12:11:59 +02:00
viewMapBuildingsMenuItem.Visible = plugin.GameType != GameType.SoleSurvivor;
viewIndicatorsFootballAreaMenuItem.Visible = plugin.GameType == GameType.SoleSurvivor;
2022-09-01 13:05:49 +02:00
viewIndicatorsBuildingFakeLabelsMenuItem.Visible = plugin.GameType == GameType.RedAlert;
2022-09-25 12:11:59 +02:00
viewIndicatorsBuildingRebuildLabelsMenuItem.Visible = plugin.GameType != GameType.SoleSurvivor;
2022-09-01 13:05:49 +02:00
}
2022-09-25 12:11:59 +02:00
// Special rules per game.
2022-09-01 13:05:49 +02:00
// Tools
2020-09-11 23:46:04 +03:00
availableToolTypes = ToolType.None;
if (plugin != null)
{
2022-09-01 13:05:49 +02:00
TheaterType th = plugin.Map.Theater;
2020-09-11 23:46:04 +03:00
availableToolTypes |= ToolType.Waypoint;
2022-09-01 13:05:49 +02:00
if (plugin.Map.TemplateTypes.Any(t => t.Theaters == null || t.Theaters.Contains(th))) availableToolTypes |= ToolType.Map;
2020-09-11 23:46:04 +03:00
if (plugin.Map.SmudgeTypes.Any()) availableToolTypes |= ToolType.Smudge;
if (plugin.Map.OverlayTypes.Any(t => t.IsPlaceable && (!Globals.FilterTheaterObjects || t.Theaters == null || t.Theaters.Contains(th)))) availableToolTypes |= ToolType.Overlay;
if (plugin.Map.TerrainTypes.Any(t => !Globals.FilterTheaterObjects || t.Theaters == null || t.Theaters.Contains(th))) availableToolTypes |= ToolType.Terrain;
2020-09-11 23:46:04 +03:00
if (plugin.Map.InfantryTypes.Any()) availableToolTypes |= ToolType.Infantry;
if (plugin.Map.UnitTypes.Any()) availableToolTypes |= ToolType.Unit;
if (plugin.Map.BuildingTypes.Any(t => !Globals.FilterTheaterObjects || t.Theaters == null || t.Theaters.Contains(th))) availableToolTypes |= ToolType.Building;
if (plugin.Map.OverlayTypes.Any(t => t.IsResource && (!Globals.FilterTheaterObjects || t.Theaters == null || t.Theaters.Contains(th)))) availableToolTypes |= ToolType.Resources;
if (plugin.Map.OverlayTypes.Any(t => t.IsWall && (!Globals.FilterTheaterObjects || t.Theaters == null || t.Theaters.Contains(th)))) availableToolTypes |= ToolType.Wall;
// Always allow celltrigger tool, even if triggers list is empty; it contains a tooltip saying which triggers are eligible.
availableToolTypes |= ToolType.CellTrigger;
2022-09-19 12:23:44 +02:00
availableToolTypes |= ToolType.Select;
2020-09-11 23:46:04 +03:00
}
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()
{
2022-09-01 13:05:49 +02:00
// Menu items
fileSaveMenuItem.Enabled = false;
fileSaveAsMenuItem.Enabled = false;
filePublishMenuItem.Enabled = false;
fileExportMenuItem.Enabled = false;
editUndoMenuItem.Enabled = false;
editRedoMenuItem.Enabled = false;
editClearUndoRedoMenuItem.Enabled = false;
2022-09-01 13:05:49 +02:00
settingsMapSettingsMenuItem.Enabled = false;
settingsTeamTypesMenuItem.Enabled = false;
settingsTriggersMenuItem.Enabled = false;
toolsPowerMenuItem.Enabled = false;
toolsExportImageMenuItem.Enabled = false;
developerGoToINIMenuItem.Enabled = false;
developerDebugShowOverlapCellsMenuItem.Enabled = false;
developerGenerateMapPreviewDirectoryMenuItem.Enabled = false;
2022-09-25 12:11:59 +02:00
viewMapBuildingsMenuItem.Visible = plugin == null || plugin.GameType != GameType.SoleSurvivor;
viewIndicatorsBuildingFakeLabelsMenuItem.Visible = plugin == null || plugin.GameType == GameType.RedAlert;
viewIndicatorsBuildingRebuildLabelsMenuItem.Visible = plugin == null || plugin.GameType != GameType.SoleSurvivor;
viewIndicatorsFootballAreaMenuItem.Visible = plugin == null || plugin.GameType == GameType.SoleSurvivor;
2022-09-01 13:05:49 +02:00
// Tools
2020-09-14 13:40:09 +03:00
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);
if (!found || (toolDialog is Form toolFrm && toolFrm.IsDisposed))
2020-09-14 13:40:09 +03:00
{
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, plugin);
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;
2022-09-19 12:23:44 +02:00
case ToolType.Select:
{
toolDialog = null; // new SelectToolDialog(this);
}
break;
2020-09-14 13:40:09 +03:00
}
if (toolDialog != null)
{
toolForms[ActiveToolType] = toolDialog;
2020-09-14 13:40:09 +03:00
}
2020-09-11 23:46:04 +03:00
}
2022-09-01 13:05:49 +02:00
MapLayerFlag active = ActiveLayers;
// Save some processing by just always removing this one.
2022-09-22 01:26:46 +02:00
if (plugin.GameType == GameType.TiberianDawn || plugin.GameType == GameType.SoleSurvivor)
2022-09-01 13:05:49 +02:00
{
active &= ~MapLayerFlag.BuildingFakes;
}
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;
2022-09-19 12:23:44 +02:00
// Creates the actual Tool class
2022-09-01 13:05:49 +02:00
toolDialog.Initialize(mapPanel, active, 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;
}
2022-09-22 01:26:46 +02:00
if (plugin.IsMegaMap)
2020-09-11 23:46:04 +03:00
{
2022-09-22 01:26:46 +02:00
mapPanel.MaxZoom = 16;
mapPanel.ZoomStep = 0.2;
}
else
{
mapPanel.MaxZoom = 8;
mapPanel.ZoomStep = 0.15;
2020-09-11 23:46:04 +03:00
}
// 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 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;
2022-09-01 13:05:49 +02:00
if (!viewIndicatorsBoundariesMenuItem.Checked)
2020-09-11 23:46:04 +03:00
{
layers &= ~MapLayerFlag.Boundaries;
}
2022-09-01 13:05:49 +02:00
if (!viewMapBuildingsMenuItem.Checked)
2020-09-11 23:46:04 +03:00
{
2022-09-01 13:05:49 +02:00
layers &= ~MapLayerFlag.Buildings;
}
if (!viewMapUnitsMenuItem.Checked)
{
layers &= ~MapLayerFlag.Units;
}
if (!viewMapInfantryMenuItem.Checked)
{
layers &= ~MapLayerFlag.Infantry;
2020-09-11 23:46:04 +03:00
}
2022-09-01 13:05:49 +02:00
if (!viewMapTerrainMenuItem.Checked)
2020-09-11 23:46:04 +03:00
{
layers &= ~MapLayerFlag.Terrain;
}
2022-09-01 13:05:49 +02:00
if (!viewMapOverlayMenuItem.Checked)
{
layers &= ~MapLayerFlag.OverlayAll;
}
if (!viewMapSmudgeMenuItem.Checked)
{
layers &= ~MapLayerFlag.Smudge;
}
2022-09-22 01:26:46 +02:00
if (!viewMapWaypointsMenuItem.Checked)
2020-09-11 23:46:04 +03:00
{
layers &= ~MapLayerFlag.Waypoints;
}
2022-09-22 01:26:46 +02:00
if (!viewIndicatorsWaypointsMenuItem.Checked)
{
layers &= ~MapLayerFlag.WaypointsIndic;
}
2022-09-01 13:05:49 +02:00
if (!viewIndicatorsCellTriggersMenuItem.Checked)
2020-09-11 23:46:04 +03:00
{
layers &= ~MapLayerFlag.CellTriggers;
}
2022-09-01 13:05:49 +02:00
if (!viewIndicatorsObjectTriggersMenuItem.Checked)
2020-09-11 23:46:04 +03:00
{
layers &= ~MapLayerFlag.TechnoTriggers;
}
2022-09-01 13:05:49 +02:00
if (!viewIndicatorsBuildingFakeLabelsMenuItem.Checked)
{
layers &= ~MapLayerFlag.BuildingFakes;
}
if (!viewIndicatorsBuildingRebuildLabelsMenuItem.Checked)
{
layers &= ~MapLayerFlag.BuildingRebuild;
}
2022-09-25 12:11:59 +02:00
if (!viewIndicatorsFootballAreaMenuItem.Checked)
{
layers &= ~MapLayerFlag.FootballArea;
}
2020-09-11 23:46:04 +03:00
ActiveLayers = layers;
}
2022-09-01 13:05:49 +02:00
private void ViewMenuItem_CheckedChanged(object sender, EventArgs e)
2020-09-11 23:46:04 +03:00
{
UpdateVisibleLayers();
}
2022-09-01 13:05:49 +02:00
private void ViewMapEnableAllMenuItem_Click(object sender, EventArgs e)
{
ITool activeTool = this.activeTool;
try
{
// Suppress updates.
this.activeTool = null;
viewMapBuildingsMenuItem.Checked = true;
viewMapUnitsMenuItem.Checked = true;
viewMapInfantryMenuItem.Checked = true;
viewMapTerrainMenuItem.Checked = true;
viewMapOverlayMenuItem.Checked = true;
viewMapSmudgeMenuItem.Checked = true;
2022-09-22 01:26:46 +02:00
viewMapWaypointsMenuItem.Checked = true;
2022-09-01 13:05:49 +02:00
}
finally
{
// Re-enable tool, force refresh.
MapLayerFlag layerBackup = this.activeLayers;
// Clear without refresh
this.activeLayers = MapLayerFlag.None;
// Restore tool
this.activeTool = activeTool;
// Set with refresh
ActiveLayers = layerBackup;
}
}
private void ViewMapDisableAllMenuItem_Click(object sender, EventArgs e)
{
ITool activeTool = this.activeTool;
try
{
// Suppress updates.
this.activeTool = null;
viewMapBuildingsMenuItem.Checked = false;
viewMapUnitsMenuItem.Checked = false;
viewMapInfantryMenuItem.Checked = false;
viewMapTerrainMenuItem.Checked = false;
viewMapOverlayMenuItem.Checked = false;
viewMapSmudgeMenuItem.Checked = false;
2022-09-22 01:26:46 +02:00
viewMapWaypointsMenuItem.Checked = false;
2022-09-01 13:05:49 +02:00
}
finally
{
// Re-enable tool, force refresh.
MapLayerFlag layerBackup = this.activeLayers;
// Clear without refresh
this.activeLayers = MapLayerFlag.None;
// Restore tool
this.activeTool = activeTool;
// Set with refresh
ActiveLayers = layerBackup;
}
}
private void ViewIndicatorsEnableAllToolStripMenuItem_Click(Object sender, EventArgs e)
{
ITool activeTool = this.activeTool;
try
{
// Suppress updates.
this.activeTool = null;
viewIndicatorsBoundariesMenuItem.Checked = true;
viewIndicatorsWaypointsMenuItem.Checked = true;
viewIndicatorsCellTriggersMenuItem.Checked = true;
viewIndicatorsObjectTriggersMenuItem.Checked = true;
viewIndicatorsBuildingFakeLabelsMenuItem.Checked = true;
viewIndicatorsBuildingRebuildLabelsMenuItem.Checked = true;
}
finally
{
// Re-enable tool, force refresh.
MapLayerFlag layerBackup = this.activeLayers;
// Clear without refresh
this.activeLayers = MapLayerFlag.None;
// Restore tool
this.activeTool = activeTool;
// Set with refresh
ActiveLayers = layerBackup;
}
}
private void ViewIndicatorsDisableAllToolStripMenuItem_Click(Object sender, EventArgs e)
{
ITool activeTool = this.activeTool;
try
{
// Suppress updates.
this.activeTool = null;
viewIndicatorsBoundariesMenuItem.Checked = false;
viewIndicatorsWaypointsMenuItem.Checked = false;
viewIndicatorsCellTriggersMenuItem.Checked = false;
viewIndicatorsObjectTriggersMenuItem.Checked = false;
viewIndicatorsBuildingFakeLabelsMenuItem.Checked = false;
viewIndicatorsBuildingRebuildLabelsMenuItem.Checked = false;
}
finally
{
// Re-enable tool, force refresh.
MapLayerFlag layerBackup = this.activeLayers;
// Clear without refresh
this.activeLayers = MapLayerFlag.None;
// Restore tool
this.activeTool = activeTool;
// Set with refresh
ActiveLayers = layerBackup;
}
}
2020-09-11 23:46:04 +03:00
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-09-25 12:11:59 +02:00
if (plugin.GameType == GameType.SoleSurvivor)
{
MessageBox.Show("Sole Survivor maps cannot be published to Steam; they are not usable by the C&C Remastered Collection.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (plugin.GameType == GameType.TiberianDawn && plugin.IsMegaMap)
{
//if (DialogResult.Yes != MessageBox.Show("Megamaps are not supported by the C&C Remastered Collection without modding! Are you sure you want to publish a map that will be incompatible with the standard unmodded game?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2))
//{
// return;
//}
MessageBox.Show("Tiberian Dawn megamaps cannot be published to Steam; they are not usable by the C&C Remastered Collection without modding, and may cause issues on the official servers.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
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);
return;
}
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)
{
2022-09-19 12:23:44 +02:00
if (Form.ActiveForm != null)
{
mainToolStrip.Focus();
}
2020-09-11 23:46:04 +03:00
}
private void MainForm_Shown(object sender, System.EventArgs e)
{
2022-09-01 13:05:49 +02:00
ClearAllTools();
RefreshAvailableTools();
UpdateUndoRedo();
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();
2022-09-01 13:05:49 +02:00
if (e.Cancel)
{
return;
}
// Restore default icons, then dispose custom ones.
// Form dispose should take care of the default ones.
LoadNewIcon(mapToolStripButton, null, null, 0);
LoadNewIcon(smudgeToolStripButton, null, null, 1);
LoadNewIcon(overlayToolStripButton, null, null, 2);
LoadNewIcon(terrainToolStripButton, null, null, 3);
LoadNewIcon(infantryToolStripButton, null, null, 4);
LoadNewIcon(unitToolStripButton, null, null, 5);
LoadNewIcon(buildingToolStripButton, null, null, 6);
LoadNewIcon(resourcesToolStripButton, null, null, 7);
LoadNewIcon(wallsToolStripButton, null, null, 8);
LoadNewIcon(waypointsToolStripButton, null, null, 9);
LoadNewIcon(cellTriggersToolStripButton, null, null, 10);
List<Bitmap> toDispose = new List<Bitmap>();
foreach (int key in theaterIcons.Keys)
{
toDispose.Add(theaterIcons[key]);
}
theaterIcons.Clear();
foreach (Bitmap bm in toDispose)
{
try
{
bm.Dispose();
}
catch
{
// Ignore
}
}
2020-09-11 23:46:04 +03:00
}
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:
{
String errors = plugin.Validate();
if (errors != null)
{
MessageBox.Show(errors, "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
2020-09-11 23:46:04 +03:00
if (string.IsNullOrEmpty(filename))
{
fileSaveAsMenuItem.PerformClick();
}
else
{
fileSaveMenuItem.PerformClick();
}
}
break;
case DialogResult.No:
break;
case DialogResult.Cancel:
cancel = true;
break;
}
}
return !cancel;
}
public void UpdateStatus()
{
SetTitle();
}
2022-09-01 13:05:49 +02:00
private void LoadIcons(IGamePlugin plugin)
{
TemplateType template = plugin.Map.TemplateTypes.Where(tt => (tt.Flag & TemplateTypeFlag.Clear) != TemplateTypeFlag.Clear && tt.IconWidth == 1 && tt.IconHeight == 1
&& (tt.Theaters == null || tt.Theaters.Contains(plugin.Map.Theater))).OrderBy(tt => tt.Name).FirstOrDefault();
2022-09-01 13:05:49 +02:00
Tile templateTile = null;
if (template != null) Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, template.Name, 0, out templateTile, false, true);
SmudgeType smudge = plugin.Map.SmudgeTypes.Where(sm => !sm.IsAutoBib && sm.Icons == 1 && sm.Size.Width == 1 && sm.Size.Height == 1
&& (sm.Theaters == null || sm.Theaters.Contains(plugin.Map.Theater))).OrderBy(sm => sm.ID).FirstOrDefault();
OverlayType overlay = plugin.Map.OverlayTypes.Where(ov => (ov.Flag & OverlayTypeFlag.Crate) == OverlayTypeFlag.Crate
&& (ov.Theaters == null || ov.Theaters.Contains(plugin.Map.Theater))).OrderBy(ov => ov.ID).FirstOrDefault();
2022-09-25 12:11:59 +02:00
if (overlay == null) {
overlay = plugin.Map.OverlayTypes.Where(ov => (ov.Flag & OverlayTypeFlag.Flag) == OverlayTypeFlag.Flag
&& (ov.Theaters == null || ov.Theaters.Contains(plugin.Map.Theater))).OrderBy(ov => ov.ID).FirstOrDefault();
}
2022-09-01 13:05:49 +02:00
Tile overlayTile = null;
if (overlay != null) Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, overlay.Name, 0, out overlayTile, false, true);
TerrainType terrain = plugin.Map.TerrainTypes.Where(tr => tr.Theaters == null || tr.Theaters.Contains(plugin.Map.Theater)).OrderBy(tr => tr.ID).FirstOrDefault();;
2022-09-01 13:05:49 +02:00
InfantryType infantry = plugin.Map.InfantryTypes.FirstOrDefault();
UnitType unit = plugin.Map.UnitTypes.FirstOrDefault();
BuildingType building = plugin.Map.BuildingTypes.Where(bl => bl.Size.Width == 2 && bl.Size.Height == 2
&& (bl.Theaters == null || bl.Theaters.Contains(plugin.Map.Theater))).OrderBy(bl => bl.ID).FirstOrDefault();
OverlayType resource = plugin.Map.OverlayTypes.Where(ov => (ov.Flag & OverlayTypeFlag.TiberiumOrGold) == OverlayTypeFlag.TiberiumOrGold
&& (ov.Theaters == null || ov.Theaters.Contains(plugin.Map.Theater))).OrderBy(ov => ov.ID).FirstOrDefault();
OverlayType wall = plugin.Map.OverlayTypes.Where(ov => (ov.Flag & OverlayTypeFlag.Wall) == OverlayTypeFlag.Wall
&& (ov.Theaters == null || ov.Theaters.Contains(plugin.Map.Theater))).OrderBy(ov => ov.ID).FirstOrDefault();
2022-09-01 13:05:49 +02:00
Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, "beacon", 0, out Tile waypoint, false, true);
Globals.TheTilesetManager.GetTileData(plugin.Map.Theater.Tilesets, "mine", 3, out Tile cellTrigger, false, true);
LoadNewIcon(mapToolStripButton, templateTile?.Image, plugin, 0);
LoadNewIcon(smudgeToolStripButton, smudge?.Thumbnail, plugin, 1);
LoadNewIcon(overlayToolStripButton, overlayTile?.Image, plugin, 2);
LoadNewIcon(terrainToolStripButton, terrain?.Thumbnail, plugin, 3);
LoadNewIcon(infantryToolStripButton, infantry?.Thumbnail, plugin, 4);
LoadNewIcon(unitToolStripButton, unit?.Thumbnail, plugin, 5);
LoadNewIcon(buildingToolStripButton, building?.Thumbnail, plugin, 6);
LoadNewIcon(resourcesToolStripButton, resource?.Thumbnail, plugin, 7);
LoadNewIcon(wallsToolStripButton, wall?.Thumbnail, plugin, 8);
LoadNewIcon(waypointsToolStripButton, waypoint?.Image, plugin, 9);
LoadNewIcon(cellTriggersToolStripButton, cellTrigger?.Image, plugin, 10);
2022-09-19 12:23:44 +02:00
// The Texture manager returns a clone of its own cached image. The Tileset manager caches those clones,
// and is responsible for their cleanup, but if we use it directly it needs to be disposed.
// Icon: chrono cursor from TEXTURES_SRGB.MEG
using (Bitmap select = Globals.TheTextureManager.GetTexture(@"DATA\ART\TEXTURES\SRGB\ICON_SELECT_GREEN_04.DDS", null, false).Item1)
{
LoadNewIcon(selectToolStripButton, select, plugin, 11, false);
}
2022-09-01 13:05:49 +02:00
}
private void LoadNewIcon(ViewToolStripButton button, Bitmap image, IGamePlugin plugin, int index)
2022-09-19 12:23:44 +02:00
{
LoadNewIcon(button, image, plugin, index, true);
}
private void LoadNewIcon(ViewToolStripButton button, Bitmap image, IGamePlugin plugin, int index, bool crop)
2022-09-01 13:05:49 +02:00
{
if (image == null || plugin == null)
{
if (button.Tag is Image img)
{
button.Image = img;
}
return;
}
int id = ((int)plugin.GameType) << 8 | Enumerable.Range(0, plugin.Map.TheaterTypes.Count).FirstOrDefault(i => plugin.Map.TheaterTypes[i].Equals(plugin.Map.Theater)) << 4 | index;
if (button.Tag == null)
{
// Backup default image
button.Tag = button.Image;
}
if (theaterIcons.TryGetValue(id, out Bitmap bm))
{
button.Image = bm;
}
else
{
2022-09-19 12:23:44 +02:00
Rectangle opaqueBounds = crop ? TextureManager.CalculateOpaqueBounds(image) : new Rectangle(0, 0, image.Width, image.Height);
2022-09-01 13:05:49 +02:00
Bitmap img = image.FitToBoundingBox(opaqueBounds, 24, 24, Color.Transparent);
theaterIcons[id] = img;
button.Image = img;
}
}
2020-09-11 23:46:04 +03:00
}
}