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
2020-09-14 18:13:57 +03:00
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
{
2022-08-22 11:22:55 +02:00
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 ;
2020-09-14 18:13:57 +03:00
private ViewToolStripButton [ ] viewToolStripButtons ;
2020-09-14 13:40:09 +03:00
2020-09-11 23:46:04 +03:00
private IGamePlugin plugin ;
2022-08-10 23:59:53 +02:00
private FileType loadedFileType ;
2020-09-11 23:46:04 +03:00
private string filename ;
private readonly MRU mru ;
2022-09-08 09:55:29 +02:00
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 ( ) ;
}
2022-07-12 07:27:13 +02:00
public MainForm ( String fileToOpen )
2020-09-11 23:46:04 +03:00
{
2022-07-12 07:27:13 +02:00
this . filename = fileToOpen ;
2020-09-11 23:46:04 +03:00
InitializeComponent ( ) ;
2022-08-11 22:49:22 +02:00
// 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 > ( ) ;
2020-09-14 18:13:57 +03:00
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-14 18:13:57 +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 ;
}
2022-08-08 11:53:51 +02:00
private void SetTitle ( )
{
2022-09-19 12:23:44 +02:00
const string noname = "Untitled" ;
2022-09-08 09:55:29 +02:00
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
}
2022-09-08 09:55:29 +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 ) ;
2022-09-08 09:55:29 +02:00
}
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 )
{
2022-09-08 09:55:29 +02:00
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 )
{
2022-09-08 09:55:29 +02:00
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 ;
2022-09-08 09:55:29 +02:00
editClearUndoRedoMenuItem . Enabled = url . CanUndo | | url . CanRedo ;
2020-09-11 23:46:04 +03:00
}
private void UndoRedo_Updated ( object sender , EventArgs e )
{
UpdateUndoRedo ( ) ;
}
2022-09-08 09:55:29 +02:00
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 )
{
2022-09-29 07:50:41 +02:00
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" ) ;
2022-08-22 11:22:55 +02:00
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 ( ) ;
}
}
2022-09-08 09:55:29 +02:00
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
}
}
2022-09-08 09:55:29 +02:00
private void FileSaveMenuItem_Click ( object sender , EventArgs e )
2020-09-11 23:46:04 +03:00
{
if ( plugin = = null )
{
return ;
}
2022-08-16 00:15:46 +02:00
if ( string . IsNullOrEmpty ( filename ) | | ! Directory . Exists ( Path . GetDirectoryName ( filename ) ) )
2020-09-11 23:46:04 +03:00
{
fileSaveAsMenuItem . PerformClick ( ) ;
}
else
{
2022-08-14 15:12:30 +02:00
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 ) ;
2022-08-14 15:12:30 +02:00
return ;
}
2020-09-11 23:46:04 +03:00
var fileInfo = new FileInfo ( filename ) ;
2022-08-10 23:59:53 +02:00
if ( SaveFile ( fileInfo . FullName , loadedFileType ) )
2020-09-11 23:46:04 +03:00
{
mru . Add ( fileInfo ) ;
}
else
{
mru . Remove ( fileInfo ) ;
}
}
}
2022-09-08 09:55:29 +02:00
private void FileSaveAsMenuItem_Click ( object sender , EventArgs e )
2020-09-11 23:46:04 +03:00
{
if ( plugin = = null )
{
return ;
}
2022-08-14 15:12:30 +02:00
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 ) ;
2022-08-10 23:59:53 +02:00
if ( SaveFile ( fileInfo . FullName , FileType . INI ) )
2020-09-11 23:46:04 +03:00
{
mru . Add ( fileInfo ) ;
}
else
{
mru . Remove ( fileInfo ) ;
}
}
}
2022-09-08 09:55:29 +02:00
private void FileExportMenuItem_Click ( object sender , EventArgs e )
2020-09-11 23:46:04 +03:00
{
if ( plugin = = null )
{
return ;
}
2022-08-14 15:12:30 +02:00
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
}
}
2022-09-08 09:55:29 +02:00
private void FileExitMenuItem_Click ( object sender , EventArgs e )
2020-09-11 23:46:04 +03:00
{
Close ( ) ;
}
2022-09-08 09:55:29 +02:00
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
}
}
2022-09-08 09:55:29 +02: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
}
}
2022-09-08 09:55:29 +02: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 ;
}
2022-09-08 09:55:29 +02:00
bool expansionEnabled = plugin . Map . BasicSection . ExpansionEnabled ;
2022-09-10 02:47:12 +02:00
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 ) ;
}
2022-09-10 02:47:12 +02:00
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 ( ) ;
}
2022-09-10 02:47:12 +02:00
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-10 02:47:12 +02:00
}
2022-09-19 12:23:44 +02:00
plugin . Dirty = hasChanges ;
2020-09-11 23:46:04 +03:00
}
}
2022-09-10 02:47:12 +02:00
if ( rulesChanged | | ( expansionEnabled & & ! plugin . Map . BasicSection . ExpansionEnabled ) )
2022-09-08 09:55:29 +02:00
{
2022-09-10 02:47:12 +02:00
// If Aftermath units were disabled, we can't guarantee none of them are still in
2022-09-08 09:55:29 +02:00
// 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.
2022-09-10 02:47:12 +02:00
// Rule changes will clear undo to avoid conflicts with placed smudge types.
2022-09-08 09:55:29 +02:00
url . Clear ( ) ;
}
2020-09-11 23:46:04 +03:00
}
2022-09-08 09:55:29 +02: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
}
}
2022-09-08 09:55:29 +02: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 ( ) ;
}
}
}
}
2022-09-08 09:55:29 +02:00
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 ) ;
}
}
2022-09-10 02:47:12 +02:00
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 ( ) ) ;
}
}
2022-09-08 09:55:29 +02:00
private void ToolsExportImage_Click ( Object sender , EventArgs e )
2022-09-01 13:05:49 +02:00
{
if ( plugin = = null )
{
return ;
}
2022-09-30 14:52:52 +02:00
using ( ImageExportDialog imex = new ImageExportDialog ( plugin , activeLayers , filename ) )
2022-09-01 13:05:49 +02:00
{
2022-09-30 14:52:52 +02:00
imex . ShowDialog ( this ) ;
2020-09-11 23:46:04 +03:00
}
}
private void Mru_FileSelected ( object sender , FileInfo e )
{
2022-07-12 07:27:13 +02:00
OpenFile ( e . FullName , true ) ;
2020-09-11 23:46:04 +03:00
}
2022-09-08 09:55:29 +02: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 ) ;
2022-08-11 22:49:22 +02:00
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 (
2022-08-11 22:49:22 +02:00
( 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 ) ;
}
2022-07-08 22:27:50 +02:00
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
{
2020-09-14 14:11:34 +03:00
cellStatusLabel . Text = "No cell" ;
2020-09-11 23:46:04 +03:00
}
}
}
2022-07-12 07:27:13 +02: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 )
{
2022-08-10 23:59:53 +02:00
try
{
2022-08-18 00:41:47 +02:00
if ( ! File . Exists ( loadFilename ) )
2022-08-10 23:59:53 +02:00
{
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 )
{
2022-08-10 23:59:53 +02:00
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.
2022-08-18 00:41:47 +02:00
fileType = FileType . INI ;
2022-08-10 23:59:53 +02:00
}
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
{
2022-09-29 07:50:41 +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 :
2022-08-10 23:59:53 +02:00
{
try
2020-09-11 23:46:04 +03:00
{
2022-08-10 23:59:53 +02:00
using ( var megafile = new Megafile ( loadFilename ) )
2020-09-11 23:46:04 +03:00
{
2022-08-10 23:59:53 +02:00
if ( megafile . Any ( f = > Path . GetExtension ( f ) . ToLower ( ) = = ".mpr" ) )
2020-09-11 23:46:04 +03:00
{
2022-08-10 23:59:53 +02:00
gameType = GameType . RedAlert ;
}
else
{
gameType = GameType . TiberianDawn ;
2020-09-11 23:46:04 +03:00
}
}
}
2022-08-10 23:59:53 +02:00
catch ( FileNotFoundException )
{
return false ;
}
2020-09-11 23:46:04 +03:00
break ;
2022-08-10 23:59:53 +02:00
}
2020-09-11 23:46:04 +03:00
#endif
}
2022-09-29 07:50:41 +02:00
bool isTdMegaMap = false ;
2022-09-20 18:40:27 +02:00
if ( gameType = = GameType . TiberianDawn )
{
2022-09-29 07:50:41 +02:00
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 )
{
2022-09-29 07:50:41 +02:00
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 :
2022-09-29 07:50:41 +02:00
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 :
2022-09-29 07:50:41 +02:00
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
}
2022-08-22 11:22:55 +02: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 )
{
2022-08-22 11:22:55 +02:00
using ( ErrorMessageBox emb = new ErrorMessageBox ( ) )
{
emb . Errors = errors ;
2022-09-01 13:05:49 +02:00
emb . StartPosition = FormStartPosition . CenterParent ;
emb . ShowDialog ( this ) ;
2022-08-22 11:22:55 +02:00
}
2020-09-11 23:46:04 +03:00
}
}
2022-08-15 13:02:44 +02: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 ;
2022-09-13 10:44:15 +02:00
#if DEBUG
2022-09-25 12:11:59 +02:00
errorMessage + = "\n\n" + ex . StackTrace ;
2022-09-13 10:44:15 +02:00
#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 ;
2022-08-10 23:59:53 +02:00
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 ( )
2022-08-18 00:41:47 +02:00
{
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.
}
}
2022-08-10 23:59:53 +02:00
private bool SaveFile ( string saveFilename , FileType inputNameType )
2020-09-11 23:46:04 +03:00
{
2022-08-14 15:12:30 +02: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 )
{
2022-08-10 23:59:53 +02:00
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 ;
}
2020-09-12 00:57:57 +03:00
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 ;
2022-09-08 09:55:29 +02:00
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 ;
2022-09-12 15:14:56 +02:00
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 ;
2022-09-12 15:14:56 +02:00
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.
2022-08-14 19:15:17 +02:00
availableToolTypes | = ToolType . CellTrigger ;
2022-09-19 12:23:44 +02:00
availableToolTypes | = ToolType . Select ;
2020-09-11 23:46:04 +03:00
}
2020-09-14 18:13:57 +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 ;
2022-09-08 09:55:29 +02:00
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 ) ;
2022-09-10 02:47:12 +02:00
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
{
2022-07-12 07:27:13 +02: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
{
2022-08-12 12:12:06 +02: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
{
2022-07-12 07:27:13 +02: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
{
2022-07-12 07:27:13 +02: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
{
2022-07-12 07:27:13 +02: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
{
2022-07-12 07:27:13 +02:00
toolDialog = new InfantryToolDialog ( this , plugin ) ;
2020-09-14 13:40:09 +03:00
}
break ;
case ToolType . Unit :
{
2022-07-12 07:27:13 +02:00
toolDialog = new UnitToolDialog ( this , plugin ) ;
2020-09-14 13:40:09 +03:00
}
break ;
case ToolType . Building :
{
2022-07-12 07:27:13 +02:00
toolDialog = new BuildingToolDialog ( this , plugin ) ;
2020-09-14 13:40:09 +03:00
}
break ;
case ToolType . Wall :
{
2022-07-12 07:27:13 +02:00
toolDialog = new WallsToolDialog ( this ) ;
2020-09-14 13:40:09 +03:00
}
break ;
case ToolType . Waypoint :
{
2022-07-12 07:27:13 +02:00
toolDialog = new WaypointsToolDialog ( this ) ;
2020-09-14 13:40:09 +03:00
}
break ;
case ToolType . CellTrigger :
{
2022-07-12 07:27:13 +02:00
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 )
{
2022-09-10 02:47:12 +02:00
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 ( ) ;
2022-07-17 00:24:28 +02:00
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
}
2020-09-14 18:13:57 +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 ( ) ;
}
2022-07-17 00:24:28 +02:00
private void ClampActiveToolForm ( )
2020-09-11 23:46:04 +03:00
{
2022-07-17 00:24:28 +02:00
ClampForm ( activeToolForm ) ;
}
public static void ClampForm ( Form toolform )
{
if ( toolform = = null )
2020-09-11 23:46:04 +03:00
{
return ;
}
2022-07-17 00:24:28 +02:00
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 ;
}
2022-07-17 00:24:28 +02:00
toolform . DesktopBounds = bounds ;
2020-09-11 23:46:04 +03:00
}
private void ActiveToolForm_ResizeEnd ( object sender , EventArgs e )
{
2022-07-17 00:24:28 +02:00
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 ;
}
2020-09-14 18:13:57 +03:00
ActiveToolType = ( ( ViewToolStripButton ) sender ) . ToolType ;
2020-09-11 23:46:04 +03:00
}
2022-07-12 07:27:13 +02: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 ;
}
2022-09-29 07:50:41 +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 ) ;
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
}
2022-08-14 19:15:17 +02:00
2022-07-12 07:27:13 +02:00
private void MainForm_Shown ( object sender , System . EventArgs e )
{
2022-09-01 13:05:49 +02:00
ClearAllTools ( ) ;
RefreshAvailableTools ( ) ;
2022-09-08 09:55:29 +02:00
UpdateUndoRedo ( ) ;
2022-07-12 07:27:13 +02:00
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 :
{
2022-08-22 11:22:55 +02:00
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 ;
}
2022-08-22 11:22:55 +02:00
public void UpdateStatus ( )
{
SetTitle ( ) ;
}
2022-09-01 13:05:49 +02:00
private void LoadIcons ( IGamePlugin plugin )
{
2022-09-08 09:55:29 +02:00
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 ) ;
2022-09-08 09:55:29 +02:00
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 ) ;
2022-09-08 09:55:29 +02:00
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 ( ) ;
2022-09-08 09:55:29 +02:00
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
}
}