MainForm.cs 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // The Command & Conquer Map Editor and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // The Command & Conquer Map Editor and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. using MobiusEditor.Dialogs;
  15. using MobiusEditor.Event;
  16. using MobiusEditor.Interface;
  17. using MobiusEditor.Model;
  18. using MobiusEditor.Tools;
  19. using MobiusEditor.Tools.Dialogs;
  20. using MobiusEditor.Utility;
  21. using Steamworks;
  22. using System;
  23. using System.Collections.Generic;
  24. using System.ComponentModel;
  25. using System.Data;
  26. using System.Diagnostics;
  27. using System.Drawing;
  28. using System.IO;
  29. using System.Linq;
  30. using System.Text;
  31. using System.Windows.Forms;
  32. namespace MobiusEditor
  33. {
  34. public partial class MainForm : Form
  35. {
  36. [Flags]
  37. private enum ToolType
  38. {
  39. None = 0,
  40. Map = 1 << 0,
  41. Smudge = 1 << 1,
  42. Overlay = 1 << 2,
  43. Terrain = 1 << 3,
  44. Infantry = 1 << 4,
  45. Unit = 1 << 5,
  46. Building = 1 << 6,
  47. Resources = 1 << 7,
  48. Wall = 1 << 8,
  49. Waypoint = 1 << 9,
  50. CellTrigger = 1 << 10
  51. }
  52. private static readonly ToolType[] toolTypes;
  53. private ToolType availableToolTypes = ToolType.None;
  54. private ToolType activeToolType = ToolType.None;
  55. private ToolType ActiveToolType
  56. {
  57. get => activeToolType;
  58. set
  59. {
  60. var firstAvailableTool = value;
  61. if ((availableToolTypes & firstAvailableTool) == ToolType.None)
  62. {
  63. var otherAvailableToolTypes = toolTypes.Where(t => (availableToolTypes & t) != ToolType.None);
  64. firstAvailableTool = otherAvailableToolTypes.Any() ? otherAvailableToolTypes.First() : ToolType.None;
  65. }
  66. if (activeToolType != firstAvailableTool)
  67. {
  68. activeToolType = firstAvailableTool;
  69. RefreshActiveTool();
  70. }
  71. }
  72. }
  73. private MapLayerFlag activeLayers;
  74. public MapLayerFlag ActiveLayers
  75. {
  76. get => activeLayers;
  77. set
  78. {
  79. if (activeLayers != value)
  80. {
  81. activeLayers = value;
  82. if (activeTool != null)
  83. {
  84. activeTool.Layers = ActiveLayers;
  85. }
  86. }
  87. }
  88. }
  89. private ITool activeTool;
  90. private Form activeToolForm;
  91. private IGamePlugin plugin;
  92. private string filename;
  93. private readonly MRU mru;
  94. private readonly UndoRedoList<UndoRedoEventArgs> url = new UndoRedoList<UndoRedoEventArgs>();
  95. private readonly Timer steamUpdateTimer = new Timer();
  96. static MainForm()
  97. {
  98. toolTypes = ((IEnumerable<ToolType>)Enum.GetValues(typeof(ToolType))).Where(t => t != ToolType.None).ToArray();
  99. }
  100. public MainForm()
  101. {
  102. InitializeComponent();
  103. mru = new MRU("Software\\Petroglyph\\CnCRemasteredEditor", 10, fileRecentFilesMenuItem);
  104. mru.FileSelected += Mru_FileSelected;
  105. foreach (ToolStripButton toolStripButton in mainToolStrip.Items)
  106. {
  107. toolStripButton.MouseMove += mainToolStrip_MouseMove;
  108. }
  109. #if !DEVELOPER
  110. fileExportMenuItem.Visible = false;
  111. developerToolStripMenuItem.Visible = false;
  112. #endif
  113. url.Tracked += UndoRedo_Updated;
  114. url.Undone += UndoRedo_Updated;
  115. url.Redone += UndoRedo_Updated;
  116. UpdateUndoRedo();
  117. steamUpdateTimer.Interval = 500;
  118. steamUpdateTimer.Tick += SteamUpdateTimer_Tick;
  119. }
  120. private void SteamUpdateTimer_Tick(object sender, EventArgs e)
  121. {
  122. if (SteamworksUGC.IsInit)
  123. {
  124. SteamworksUGC.Service();
  125. }
  126. }
  127. protected override void OnLoad(EventArgs e)
  128. {
  129. base.OnLoad(e);
  130. RefreshAvailableTools();
  131. UpdateVisibleLayers();
  132. filePublishMenuItem.Visible = SteamworksUGC.IsInit;
  133. steamUpdateTimer.Start();
  134. }
  135. protected override void OnClosed(EventArgs e)
  136. {
  137. base.OnClosed(e);
  138. steamUpdateTimer.Stop();
  139. steamUpdateTimer.Dispose();
  140. }
  141. protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
  142. {
  143. if (keyData == Keys.Q)
  144. {
  145. mapToolStripButton.PerformClick();
  146. return true;
  147. }
  148. else if (keyData == Keys.W)
  149. {
  150. smudgeToolStripButton.PerformClick();
  151. return true;
  152. }
  153. else if (keyData == Keys.E)
  154. {
  155. overlayToolStripButton.PerformClick();
  156. return true;
  157. }
  158. else if (keyData == Keys.R)
  159. {
  160. terrainToolStripButton.PerformClick();
  161. return true;
  162. }
  163. else if (keyData == Keys.T)
  164. {
  165. infantryToolStripButton.PerformClick();
  166. return true;
  167. }
  168. else if (keyData == Keys.Y)
  169. {
  170. unitToolStripButton.PerformClick();
  171. return true;
  172. }
  173. else if (keyData == Keys.A)
  174. {
  175. buildingToolStripButton.PerformClick();
  176. return true;
  177. }
  178. else if (keyData == Keys.S)
  179. {
  180. resourcesToolStripButton.PerformClick();
  181. return true;
  182. }
  183. else if (keyData == Keys.D)
  184. {
  185. wallsToolStripButton.PerformClick();
  186. return true;
  187. }
  188. else if (keyData == Keys.F)
  189. {
  190. waypointsToolStripButton.PerformClick();
  191. return true;
  192. }
  193. else if (keyData == Keys.G)
  194. {
  195. cellTriggersToolStripButton.PerformClick();
  196. return true;
  197. }
  198. else if (keyData == (Keys.Control | Keys.Z))
  199. {
  200. if (editUndoMenuItem.Enabled)
  201. {
  202. editUndoMenuItem_Click(this, new EventArgs());
  203. }
  204. return true;
  205. }
  206. else if (keyData == (Keys.Control | Keys.Y))
  207. {
  208. if (editRedoMenuItem.Enabled)
  209. {
  210. editRedoMenuItem_Click(this, new EventArgs());
  211. }
  212. return true;
  213. }
  214. return base.ProcessCmdKey(ref msg, keyData);
  215. }
  216. private void UpdateUndoRedo()
  217. {
  218. editUndoMenuItem.Enabled = url.CanUndo;
  219. editRedoMenuItem.Enabled = url.CanRedo;
  220. }
  221. private void UndoRedo_Updated(object sender, EventArgs e)
  222. {
  223. UpdateUndoRedo();
  224. }
  225. private void fileNewMenuItem_Click(object sender, EventArgs e)
  226. {
  227. if (!PromptSaveMap())
  228. {
  229. return;
  230. }
  231. NewMapDialog nmd = new NewMapDialog();
  232. if (nmd.ShowDialog() == DialogResult.OK)
  233. {
  234. if (plugin != null)
  235. {
  236. plugin.Map.Triggers.CollectionChanged -= Triggers_CollectionChanged;
  237. plugin.Dispose();
  238. }
  239. plugin = null;
  240. Globals.TheTilesetManager.Reset();
  241. Globals.TheTextureManager.Reset();
  242. if (nmd.GameType == GameType.TiberianDawn)
  243. {
  244. Globals.TheTeamColorManager.Reset();
  245. Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
  246. plugin = new TiberianDawn.GamePlugin();
  247. plugin.New(nmd.TheaterName);
  248. }
  249. else if (nmd.GameType == GameType.RedAlert)
  250. {
  251. Globals.TheTeamColorManager.Reset();
  252. Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
  253. plugin = new RedAlert.GamePlugin();
  254. plugin.New(nmd.TheaterName);
  255. }
  256. if (SteamworksUGC.IsInit)
  257. {
  258. plugin.Map.BasicSection.Author = SteamFriends.GetPersonaName();
  259. }
  260. plugin.Map.Triggers.CollectionChanged += Triggers_CollectionChanged;
  261. mapPanel.MapImage = plugin.MapImage;
  262. filename = null;
  263. Text = "CnC TDRA Map Editor";
  264. url.Clear();
  265. ClearActiveTool();
  266. RefreshAvailableTools();
  267. RefreshActiveTool();
  268. }
  269. }
  270. private void fileOpenMenuItem_Click(object sender, EventArgs e)
  271. {
  272. if (!PromptSaveMap())
  273. {
  274. return;
  275. }
  276. var pgmFilter =
  277. #if DEVELOPER
  278. "|PGM files (*.pgm)|*.pgm"
  279. #else
  280. string.Empty
  281. #endif
  282. ;
  283. OpenFileDialog ofd = new OpenFileDialog
  284. {
  285. AutoUpgradeEnabled = false,
  286. RestoreDirectory = true
  287. };
  288. ofd.Filter = "Tiberian Dawn files (*.ini;*.bin)|*.ini;*.bin|Red Alert files (*.mpr)|*.mpr" + pgmFilter + "|All files (*.*)|*.*";
  289. if (plugin != null)
  290. {
  291. switch (plugin.GameType)
  292. {
  293. case GameType.TiberianDawn:
  294. ofd.InitialDirectory = TiberianDawn.Constants.SaveDirectory;
  295. ofd.FilterIndex = 1;
  296. break;
  297. case GameType.RedAlert:
  298. ofd.InitialDirectory = RedAlert.Constants.SaveDirectory;
  299. ofd.FilterIndex = 2;
  300. break;
  301. }
  302. }
  303. else
  304. {
  305. ofd.InitialDirectory = Globals.RootSaveDirectory;
  306. }
  307. if (ofd.ShowDialog() == DialogResult.OK)
  308. {
  309. var fileInfo = new FileInfo(ofd.FileName);
  310. if (LoadFile(fileInfo.FullName))
  311. {
  312. mru.Add(fileInfo);
  313. }
  314. else
  315. {
  316. mru.Remove(fileInfo);
  317. MessageBox.Show(string.Format("Error loading {0}.", ofd.FileName), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  318. }
  319. }
  320. }
  321. private void fileSaveMenuItem_Click(object sender, EventArgs e)
  322. {
  323. if (plugin == null)
  324. {
  325. return;
  326. }
  327. if (string.IsNullOrEmpty(filename))
  328. {
  329. fileSaveAsMenuItem.PerformClick();
  330. }
  331. else
  332. {
  333. var fileInfo = new FileInfo(filename);
  334. if (SaveFile(fileInfo.FullName))
  335. {
  336. mru.Add(fileInfo);
  337. }
  338. else
  339. {
  340. mru.Remove(fileInfo);
  341. }
  342. }
  343. }
  344. private void fileSaveAsMenuItem_Click(object sender, EventArgs e)
  345. {
  346. if (plugin == null)
  347. {
  348. return;
  349. }
  350. SaveFileDialog sfd = new SaveFileDialog
  351. {
  352. AutoUpgradeEnabled = false,
  353. RestoreDirectory = true
  354. };
  355. var filters = new List<string>();
  356. switch (plugin.GameType)
  357. {
  358. case GameType.TiberianDawn:
  359. filters.Add("Tiberian Dawn files (*.ini;*.bin)|*.ini;*.bin");
  360. sfd.InitialDirectory = TiberianDawn.Constants.SaveDirectory;
  361. break;
  362. case GameType.RedAlert:
  363. filters.Add("Red Alert files (*.mpr)|*.mpr");
  364. sfd.InitialDirectory = RedAlert.Constants.SaveDirectory;
  365. break;
  366. }
  367. filters.Add("All files (*.*)|*.*");
  368. sfd.Filter = string.Join("|", filters);
  369. if (!string.IsNullOrEmpty(filename))
  370. {
  371. sfd.InitialDirectory = Path.GetDirectoryName(filename);
  372. sfd.FileName = Path.GetFileName(filename);
  373. }
  374. if (sfd.ShowDialog() == DialogResult.OK)
  375. {
  376. var fileInfo = new FileInfo(sfd.FileName);
  377. if (SaveFile(fileInfo.FullName))
  378. {
  379. mru.Add(fileInfo);
  380. }
  381. else
  382. {
  383. mru.Remove(fileInfo);
  384. }
  385. }
  386. }
  387. private void fileExportMenuItem_Click(object sender, EventArgs e)
  388. {
  389. if (plugin == null)
  390. {
  391. return;
  392. }
  393. SaveFileDialog sfd = new SaveFileDialog
  394. {
  395. AutoUpgradeEnabled = false,
  396. RestoreDirectory = true
  397. };
  398. sfd.Filter = "MEG files (*.meg)|*.meg";
  399. if (sfd.ShowDialog() == DialogResult.OK)
  400. {
  401. plugin.Save(sfd.FileName, FileType.MEG);
  402. }
  403. }
  404. private void fileExitMenuItem_Click(object sender, EventArgs e)
  405. {
  406. Close();
  407. }
  408. private void editUndoMenuItem_Click(object sender, EventArgs e)
  409. {
  410. if (url.CanUndo)
  411. {
  412. url.Undo(new UndoRedoEventArgs(mapPanel, plugin.Map));
  413. }
  414. }
  415. private void editRedoMenuItem_Click(object sender, EventArgs e)
  416. {
  417. if (url.CanRedo)
  418. {
  419. url.Redo(new UndoRedoEventArgs(mapPanel, plugin.Map));
  420. }
  421. }
  422. private void settingsMapSettingsMenuItem_Click(object sender, EventArgs e)
  423. {
  424. if (plugin == null)
  425. {
  426. return;
  427. }
  428. var basicSettings = new PropertyTracker<BasicSection>(plugin.Map.BasicSection);
  429. var briefingSettings = new PropertyTracker<BriefingSection>(plugin.Map.BriefingSection);
  430. var houseSettingsTrackers = plugin.Map.Houses.ToDictionary(h => h, h => new PropertyTracker<House>(h));
  431. MapSettingsDialog msd = new MapSettingsDialog(plugin, basicSettings, briefingSettings, houseSettingsTrackers);
  432. if (msd.ShowDialog() == DialogResult.OK)
  433. {
  434. basicSettings.Commit();
  435. briefingSettings.Commit();
  436. foreach (var houseSettingsTracker in houseSettingsTrackers.Values)
  437. {
  438. houseSettingsTracker.Commit();
  439. }
  440. plugin.Dirty = true;
  441. }
  442. }
  443. private void settingsTeamTypesMenuItem_Click(object sender, EventArgs e)
  444. {
  445. if (plugin == null)
  446. {
  447. return;
  448. }
  449. int maxTeams = 0;
  450. switch (plugin.GameType)
  451. {
  452. case GameType.TiberianDawn:
  453. {
  454. maxTeams = TiberianDawn.Constants.MaxTeams;
  455. }
  456. break;
  457. case GameType.RedAlert:
  458. {
  459. maxTeams = RedAlert.Constants.MaxTeams;
  460. }
  461. break;
  462. }
  463. TeamTypesDialog ttd = new TeamTypesDialog(plugin, maxTeams);
  464. if (ttd.ShowDialog() == DialogResult.OK)
  465. {
  466. plugin.Map.TeamTypes.Clear();
  467. plugin.Map.TeamTypes.AddRange(ttd.TeamTypes.Select(t => t.Clone()));
  468. plugin.Dirty = true;
  469. }
  470. }
  471. private void settingsTriggersMenuItem_Click(object sender, EventArgs e)
  472. {
  473. if (plugin == null)
  474. {
  475. return;
  476. }
  477. int maxTriggers = 0;
  478. switch (plugin.GameType)
  479. {
  480. case GameType.TiberianDawn:
  481. {
  482. maxTriggers = TiberianDawn.Constants.MaxTriggers;
  483. }
  484. break;
  485. case GameType.RedAlert:
  486. {
  487. maxTriggers = RedAlert.Constants.MaxTriggers;
  488. }
  489. break;
  490. }
  491. TriggersDialog td = new TriggersDialog(plugin, maxTriggers);
  492. if (td.ShowDialog() == DialogResult.OK)
  493. {
  494. var oldTriggers =
  495. from leftTrigger in plugin.Map.Triggers
  496. join rightTrigger in td.Triggers
  497. on leftTrigger.Name equals rightTrigger.Name into result
  498. where result.Count() == 0
  499. select leftTrigger;
  500. var newTriggers =
  501. from leftTrigger in td.Triggers
  502. join rightTrigger in plugin.Map.Triggers
  503. on leftTrigger.Name equals rightTrigger.Name into result
  504. where result.Count() == 0
  505. select leftTrigger;
  506. var sameTriggers =
  507. from leftTrigger in plugin.Map.Triggers
  508. join rightTrigger in td.Triggers
  509. on leftTrigger.Name equals rightTrigger.Name
  510. select new
  511. {
  512. OldTrigger = leftTrigger,
  513. NewTrigger = rightTrigger
  514. };
  515. foreach (var oldTrigger in oldTriggers.ToArray())
  516. {
  517. plugin.Map.Triggers.Remove(oldTrigger);
  518. }
  519. foreach (var newTrigger in newTriggers.ToArray())
  520. {
  521. plugin.Map.Triggers.Add(newTrigger.Clone());
  522. }
  523. foreach (var item in sameTriggers.ToArray())
  524. {
  525. plugin.Map.Triggers.Add(item.NewTrigger.Clone());
  526. plugin.Map.Triggers.Remove(item.OldTrigger);
  527. }
  528. plugin.Dirty = true;
  529. }
  530. }
  531. private void Mru_FileSelected(object sender, FileInfo e)
  532. {
  533. if (!PromptSaveMap())
  534. {
  535. return;
  536. }
  537. if (LoadFile(e.FullName))
  538. {
  539. mru.Add(e);
  540. }
  541. else
  542. {
  543. mru.Remove(e);
  544. MessageBox.Show(string.Format("Error loading {0}.", e.FullName), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  545. }
  546. }
  547. private void mapPanel_MouseMove(object sender, MouseEventArgs e)
  548. {
  549. if (plugin != null)
  550. {
  551. var mapPoint = mapPanel.ClientToMap(e.Location);
  552. var location = new Point((int)Math.Floor((double)mapPoint.X / Globals.TileWidth), (int)Math.Floor((double)mapPoint.Y / Globals.TileHeight));
  553. if (plugin.Map.Metrics.GetCell(location, out int cell))
  554. {
  555. var sb = new StringBuilder();
  556. sb.AppendFormat("X = {0}, Y = {1}, Cell = {2}", location.X, location.Y, cell);
  557. var template = plugin.Map.Templates[cell];
  558. var templateType = template?.Type;
  559. if (templateType != null)
  560. {
  561. sb.AppendFormat(", Template = {0} ({1})", templateType.DisplayName, template.Icon);
  562. }
  563. var smudge = plugin.Map.Smudge[cell];
  564. var smudgeType = smudge?.Type;
  565. if (smudgeType != null)
  566. {
  567. sb.AppendFormat(", Smudge = {0}", smudgeType.DisplayName);
  568. }
  569. var overlay = plugin.Map.Overlay[cell];
  570. var overlayType = overlay?.Type;
  571. if (overlayType != null)
  572. {
  573. sb.AppendFormat(", Overlay = {0}", overlayType.DisplayName);
  574. }
  575. var terrain = plugin.Map.Technos[location] as Terrain;
  576. var terrainType = terrain?.Type;
  577. if (terrainType != null)
  578. {
  579. sb.AppendFormat(", Terrain = {0}", terrainType.DisplayName);
  580. }
  581. if (plugin.Map.Technos[location] is InfantryGroup infantryGroup)
  582. {
  583. var subPixel = new Point(
  584. (mapPoint.X * Globals.PixelWidth / Globals.TileWidth) % Globals.PixelWidth,
  585. (mapPoint.Y * Globals.PixelHeight / Globals.TileHeight) % Globals.PixelHeight
  586. );
  587. var i = InfantryGroup.ClosestStoppingTypes(subPixel).Cast<int>().First();
  588. if (infantryGroup.Infantry[i] != null)
  589. {
  590. sb.AppendFormat(", Infantry = {0}", infantryGroup.Infantry[i].Type.DisplayName);
  591. }
  592. }
  593. var unit = plugin.Map.Technos[location] as Unit;
  594. var unitType = unit?.Type;
  595. if (unitType != null)
  596. {
  597. sb.AppendFormat(", Unit = {0}", unitType.DisplayName);
  598. }
  599. var building = plugin.Map.Technos[location] as Building;
  600. var buildingType = building?.Type;
  601. if (buildingType != null)
  602. {
  603. sb.AppendFormat(", Building = {0}", buildingType.DisplayName);
  604. }
  605. cellStatusLabel.Text = sb.ToString();
  606. }
  607. else
  608. {
  609. cellStatusLabel.Text = string.Empty;
  610. }
  611. }
  612. }
  613. private bool LoadFile(string loadFilename)
  614. {
  615. FileType fileType = FileType.None;
  616. switch (Path.GetExtension(loadFilename).ToLower())
  617. {
  618. case ".ini":
  619. case ".mpr":
  620. fileType = FileType.INI;
  621. break;
  622. case ".bin":
  623. fileType = FileType.BIN;
  624. break;
  625. #if DEVELOPER
  626. case ".pgm":
  627. fileType = FileType.PGM;
  628. break;
  629. #endif
  630. }
  631. if (fileType == FileType.None)
  632. {
  633. return false;
  634. }
  635. GameType gameType = GameType.None;
  636. switch (fileType)
  637. {
  638. case FileType.INI:
  639. {
  640. var ini = new INI();
  641. try
  642. {
  643. using (var reader = new StreamReader(loadFilename))
  644. {
  645. ini.Parse(reader);
  646. }
  647. }
  648. catch (FileNotFoundException)
  649. {
  650. return false;
  651. }
  652. gameType = File.Exists(Path.ChangeExtension(loadFilename, ".bin")) ? GameType.TiberianDawn : GameType.RedAlert;
  653. }
  654. break;
  655. case FileType.BIN:
  656. gameType = GameType.TiberianDawn;
  657. break;
  658. #if DEVELOPER
  659. case FileType.PGM:
  660. {
  661. try
  662. {
  663. using (var megafile = new Megafile(loadFilename))
  664. {
  665. if (megafile.Any(f => Path.GetExtension(f).ToLower() == ".mpr"))
  666. {
  667. gameType = GameType.RedAlert;
  668. }
  669. else
  670. {
  671. gameType = GameType.TiberianDawn;
  672. }
  673. }
  674. }
  675. catch (FileNotFoundException)
  676. {
  677. return false;
  678. }
  679. }
  680. break;
  681. #endif
  682. }
  683. if (gameType == GameType.None)
  684. {
  685. return false;
  686. }
  687. if (plugin != null)
  688. {
  689. plugin.Map.Triggers.CollectionChanged -= Triggers_CollectionChanged;
  690. plugin.Dispose();
  691. }
  692. plugin = null;
  693. Globals.TheTilesetManager.Reset();
  694. Globals.TheTextureManager.Reset();
  695. switch (gameType)
  696. {
  697. case GameType.TiberianDawn:
  698. {
  699. Globals.TheTeamColorManager.Reset();
  700. Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
  701. plugin = new TiberianDawn.GamePlugin();
  702. }
  703. break;
  704. case GameType.RedAlert:
  705. {
  706. Globals.TheTeamColorManager.Reset();
  707. Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
  708. plugin = new RedAlert.GamePlugin();
  709. }
  710. break;
  711. }
  712. try
  713. {
  714. var errors = plugin.Load(loadFilename, fileType).ToArray();
  715. if (errors.Length > 0)
  716. {
  717. ErrorMessageBox errorMessageBox = new ErrorMessageBox { Errors = errors };
  718. errorMessageBox.ShowDialog();
  719. }
  720. }
  721. catch (Exception)
  722. {
  723. #if DEVELOPER
  724. throw;
  725. #else
  726. return false;
  727. #endif
  728. }
  729. plugin.Map.Triggers.CollectionChanged += Triggers_CollectionChanged;
  730. mapPanel.MapImage = plugin.MapImage;
  731. plugin.Dirty = false;
  732. filename = loadFilename;
  733. Text = string.Format("CnC TDRA Map Editor - {0}", filename);
  734. url.Clear();
  735. ClearActiveTool();
  736. RefreshAvailableTools();
  737. RefreshActiveTool();
  738. return true;
  739. }
  740. private bool SaveFile(string saveFilename)
  741. {
  742. FileType fileType = FileType.None;
  743. switch (Path.GetExtension(saveFilename).ToLower())
  744. {
  745. case ".ini":
  746. case ".mpr":
  747. fileType = FileType.INI;
  748. break;
  749. case ".bin":
  750. fileType = FileType.BIN;
  751. break;
  752. }
  753. if (fileType == FileType.None)
  754. {
  755. return false;
  756. }
  757. if (string.IsNullOrEmpty(plugin.Map.SteamSection.Title))
  758. {
  759. plugin.Map.SteamSection.Title = plugin.Map.BasicSection.Name;
  760. }
  761. if (!plugin.Save(saveFilename, fileType))
  762. {
  763. return false;
  764. }
  765. if (new FileInfo(saveFilename).Length > Globals.MaxMapSize)
  766. {
  767. MessageBox.Show(string.Format("Map file exceeds the maximum size of {0} bytes.", Globals.MaxMapSize), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  768. }
  769. plugin.Dirty = false;
  770. filename = saveFilename;
  771. Text = string.Format("CnC TDRA Map Editor - {0}", filename);
  772. return true;
  773. }
  774. private void RefreshAvailableTools()
  775. {
  776. availableToolTypes = ToolType.None;
  777. if (plugin != null)
  778. {
  779. availableToolTypes |= ToolType.Waypoint;
  780. if (plugin.Map.TemplateTypes.Any()) availableToolTypes |= ToolType.Map;
  781. if (plugin.Map.SmudgeTypes.Any()) availableToolTypes |= ToolType.Smudge;
  782. if (plugin.Map.OverlayTypes.Any(t => t.IsPlaceable && ((t.Theaters == null) || t.Theaters.Contains(plugin.Map.Theater)))) availableToolTypes |= ToolType.Overlay;
  783. if (plugin.Map.TerrainTypes.Any(t => t.Theaters.Contains(plugin.Map.Theater))) availableToolTypes |= ToolType.Terrain;
  784. if (plugin.Map.InfantryTypes.Any()) availableToolTypes |= ToolType.Infantry;
  785. if (plugin.Map.UnitTypes.Any()) availableToolTypes |= ToolType.Unit;
  786. if (plugin.Map.BuildingTypes.Any()) availableToolTypes |= ToolType.Building;
  787. if (plugin.Map.OverlayTypes.Any(t => t.IsResource)) availableToolTypes |= ToolType.Resources;
  788. if (plugin.Map.OverlayTypes.Any(t => t.IsWall)) availableToolTypes |= ToolType.Wall;
  789. if (plugin.Map.Triggers.Any()) availableToolTypes |= ToolType.CellTrigger;
  790. }
  791. mapToolStripButton.Enabled = (availableToolTypes & ToolType.Map) != ToolType.None;
  792. smudgeToolStripButton.Enabled = (availableToolTypes & ToolType.Smudge) != ToolType.None;
  793. overlayToolStripButton.Enabled = (availableToolTypes & ToolType.Overlay) != ToolType.None;
  794. terrainToolStripButton.Enabled = (availableToolTypes & ToolType.Terrain) != ToolType.None;
  795. infantryToolStripButton.Enabled = (availableToolTypes & ToolType.Infantry) != ToolType.None;
  796. unitToolStripButton.Enabled = (availableToolTypes & ToolType.Unit) != ToolType.None;
  797. buildingToolStripButton.Enabled = (availableToolTypes & ToolType.Building) != ToolType.None;
  798. resourcesToolStripButton.Enabled = (availableToolTypes & ToolType.Resources) != ToolType.None;
  799. wallsToolStripButton.Enabled = (availableToolTypes & ToolType.Wall) != ToolType.None;
  800. waypointsToolStripButton.Enabled = (availableToolTypes & ToolType.Waypoint) != ToolType.None;
  801. cellTriggersToolStripButton.Enabled = (availableToolTypes & ToolType.CellTrigger) != ToolType.None;
  802. ActiveToolType = activeToolType;
  803. }
  804. private void ClearActiveTool()
  805. {
  806. activeTool?.Dispose();
  807. activeTool = null;
  808. if (activeToolForm != null)
  809. {
  810. activeToolForm.ResizeEnd -= ActiveToolForm_ResizeEnd;
  811. activeToolForm.Close();
  812. activeToolForm = null;
  813. }
  814. toolStatusLabel.Text = string.Empty;
  815. }
  816. private void RefreshActiveTool()
  817. {
  818. if (plugin == null)
  819. {
  820. return;
  821. }
  822. if (activeTool == null)
  823. {
  824. activeLayers = MapLayerFlag.None;
  825. }
  826. ClearActiveTool();
  827. switch (ActiveToolType)
  828. {
  829. case ToolType.Map:
  830. {
  831. TemplateToolDialog toolDialog = new TemplateToolDialog();
  832. activeTool = new TemplateTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TemplateTypeListView, toolDialog.TemplateTypeMapPanel, mouseToolTip, plugin, url);
  833. activeToolForm = toolDialog;
  834. activeToolForm.Show(this);
  835. } break;
  836. case ToolType.Smudge:
  837. {
  838. GenericToolDialog toolDialog = new GenericToolDialog
  839. {
  840. Text = "Smudge"
  841. };
  842. toolDialog.GenericTypeComboBox.Types = plugin.Map.SmudgeTypes.Where(t => (t.Flag & SmudgeTypeFlag.Bib) == SmudgeTypeFlag.None).OrderBy(t => t.Name);
  843. activeTool = new SmudgeTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.GenericTypeComboBox, toolDialog.GenericTypeMapPanel, plugin, url);
  844. activeToolForm = toolDialog;
  845. activeToolForm.Show(this);
  846. }
  847. break;
  848. case ToolType.Overlay:
  849. {
  850. GenericToolDialog toolDialog = new GenericToolDialog
  851. {
  852. Text = "Overlay"
  853. };
  854. toolDialog.GenericTypeComboBox.Types = plugin.Map.OverlayTypes.Where(t => t.IsPlaceable && ((t.Theaters == null) || t.Theaters.Contains(plugin.Map.Theater))).OrderBy(t => t.Name);
  855. activeTool = new OverlaysTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.GenericTypeComboBox, toolDialog.GenericTypeMapPanel, plugin, url);
  856. activeToolForm = toolDialog;
  857. activeToolForm.Show(this);
  858. }
  859. break;
  860. case ToolType.Resources:
  861. {
  862. ResourcesToolDialog toolDialog = new ResourcesToolDialog();
  863. activeTool = new ResourcesTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TotalResourcesLbl, toolDialog.ResourceBrushSizeNud, toolDialog.GemsCheckBox, plugin, url);
  864. activeToolForm = toolDialog;
  865. activeToolForm.Show(this);
  866. }
  867. break;
  868. case ToolType.Terrain:
  869. {
  870. TerrainToolDialog toolDialog = new TerrainToolDialog(plugin);
  871. toolDialog.TerrainTypeComboBox.Types = plugin.Map.TerrainTypes.Where(t => t.Theaters.Contains(plugin.Map.Theater)).OrderBy(t => t.Name);
  872. activeTool = new TerrainTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TerrainTypeComboBox, toolDialog.TerrainTypeMapPanel, toolDialog.TerrainProperties, plugin, url);
  873. activeToolForm = toolDialog;
  874. activeToolForm.Show(this);
  875. }
  876. break;
  877. case ToolType.Infantry:
  878. {
  879. ObjectToolDialog toolDialog = new ObjectToolDialog(plugin)
  880. {
  881. Text = "Infantry"
  882. };
  883. toolDialog.ObjectTypeComboBox.Types = plugin.Map.InfantryTypes.OrderBy(t => t.Name);
  884. activeTool = new InfantryTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.ObjectTypeComboBox, toolDialog.ObjectTypeMapPanel, toolDialog.ObjectProperties, plugin, url);
  885. activeToolForm = toolDialog;
  886. activeToolForm.Show(this);
  887. }
  888. break;
  889. case ToolType.Unit:
  890. {
  891. ObjectToolDialog toolDialog = new ObjectToolDialog(plugin)
  892. {
  893. Text = "Units"
  894. };
  895. toolDialog.ObjectTypeComboBox.Types = plugin.Map.UnitTypes
  896. .Where(t => !t.IsFixedWing)
  897. .OrderBy(t => t.Name);
  898. activeTool = new UnitTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.ObjectTypeComboBox, toolDialog.ObjectTypeMapPanel, toolDialog.ObjectProperties, plugin, url);
  899. activeToolForm = toolDialog;
  900. activeToolForm.Show(this);
  901. }
  902. break;
  903. case ToolType.Building:
  904. {
  905. ObjectToolDialog toolDialog = new ObjectToolDialog(plugin)
  906. {
  907. Text = "Structures"
  908. };
  909. toolDialog.ObjectTypeComboBox.Types = plugin.Map.BuildingTypes
  910. .Where(t => (t.Theaters == null) || t.Theaters.Contains(plugin.Map.Theater))
  911. .OrderBy(t => t.IsFake)
  912. .ThenBy(t => t.Name);
  913. activeTool = new BuildingTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.ObjectTypeComboBox, toolDialog.ObjectTypeMapPanel, toolDialog.ObjectProperties, plugin, url);
  914. activeToolForm = toolDialog;
  915. activeToolForm.Show(this);
  916. }
  917. break;
  918. case ToolType.Wall:
  919. {
  920. GenericToolDialog toolDialog = new GenericToolDialog
  921. {
  922. Text = "Walls"
  923. };
  924. toolDialog.GenericTypeComboBox.Types = plugin.Map.OverlayTypes.Where(t => t.IsWall).OrderBy(t => t.Name);
  925. activeTool = new WallsTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.GenericTypeComboBox, toolDialog.GenericTypeMapPanel, plugin, url);
  926. activeToolForm = toolDialog;
  927. activeToolForm.Show(this);
  928. }
  929. break;
  930. case ToolType.Waypoint:
  931. {
  932. WaypointsToolDialog toolDialog = new WaypointsToolDialog();
  933. toolDialog.WaypointCombo.DataSource = plugin.Map.Waypoints.Select(w => w.Name).ToArray();
  934. activeTool = new WaypointsTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.WaypointCombo, plugin, url);
  935. activeToolForm = toolDialog;
  936. activeToolForm.Show(this);
  937. }
  938. break;
  939. case ToolType.CellTrigger:
  940. {
  941. CellTriggersToolDialog toolDialog = new CellTriggersToolDialog();
  942. toolDialog.TriggerCombo.DataSource = plugin.Map.Triggers.Select(t => t.Name).ToArray();
  943. activeTool = new CellTriggersTool(mapPanel, ActiveLayers, toolStatusLabel, toolDialog.TriggerCombo, plugin, url);
  944. activeToolForm = toolDialog;
  945. activeToolForm.Show(this);
  946. }
  947. break;
  948. }
  949. if (activeToolForm != null)
  950. {
  951. activeToolForm.ResizeEnd += ActiveToolForm_ResizeEnd;
  952. clampActiveToolForm();
  953. }
  954. switch (plugin.GameType)
  955. {
  956. case GameType.TiberianDawn:
  957. mapPanel.MaxZoom = 8;
  958. mapPanel.ZoomStep = 1;
  959. break;
  960. case GameType.RedAlert:
  961. mapPanel.MaxZoom = 16;
  962. mapPanel.ZoomStep = 2;
  963. break;
  964. }
  965. mapToolStripButton.Checked = ActiveToolType == ToolType.Map;
  966. smudgeToolStripButton.Checked = ActiveToolType == ToolType.Smudge;
  967. overlayToolStripButton.Checked = ActiveToolType == ToolType.Overlay;
  968. terrainToolStripButton.Checked = ActiveToolType == ToolType.Terrain;
  969. infantryToolStripButton.Checked = ActiveToolType == ToolType.Infantry;
  970. unitToolStripButton.Checked = ActiveToolType == ToolType.Unit;
  971. buildingToolStripButton.Checked = ActiveToolType == ToolType.Building;
  972. resourcesToolStripButton.Checked = ActiveToolType == ToolType.Resources;
  973. wallsToolStripButton.Checked = ActiveToolType == ToolType.Wall;
  974. waypointsToolStripButton.Checked = ActiveToolType == ToolType.Waypoint;
  975. cellTriggersToolStripButton.Checked = ActiveToolType == ToolType.CellTrigger;
  976. Focus();
  977. UpdateVisibleLayers();
  978. mapPanel.Invalidate();
  979. }
  980. private void clampActiveToolForm()
  981. {
  982. if (activeToolForm == null)
  983. {
  984. return;
  985. }
  986. Rectangle bounds = activeToolForm.DesktopBounds;
  987. Rectangle workingArea = Screen.FromControl(this).WorkingArea;
  988. if (bounds.Right > workingArea.Right)
  989. {
  990. bounds.X = workingArea.Right - bounds.Width;
  991. }
  992. if (bounds.X < workingArea.Left)
  993. {
  994. bounds.X = workingArea.Left;
  995. }
  996. if (bounds.Bottom > workingArea.Bottom)
  997. {
  998. bounds.Y = workingArea.Bottom - bounds.Height;
  999. }
  1000. if (bounds.Y < workingArea.Top)
  1001. {
  1002. bounds.Y = workingArea.Top;
  1003. }
  1004. activeToolForm.DesktopBounds = bounds;
  1005. }
  1006. private void ActiveToolForm_ResizeEnd(object sender, EventArgs e)
  1007. {
  1008. clampActiveToolForm();
  1009. }
  1010. private void Triggers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  1011. {
  1012. RefreshAvailableTools();
  1013. }
  1014. private void mainToolStripButton_Click(object sender, EventArgs e)
  1015. {
  1016. if (plugin == null)
  1017. {
  1018. return;
  1019. }
  1020. if (sender == mapToolStripButton)
  1021. {
  1022. ActiveToolType = ToolType.Map;
  1023. }
  1024. else if (sender == smudgeToolStripButton)
  1025. {
  1026. ActiveToolType = ToolType.Smudge;
  1027. }
  1028. else if (sender == overlayToolStripButton)
  1029. {
  1030. ActiveToolType = ToolType.Overlay;
  1031. }
  1032. else if (sender == terrainToolStripButton)
  1033. {
  1034. ActiveToolType = ToolType.Terrain;
  1035. }
  1036. else if (sender == infantryToolStripButton)
  1037. {
  1038. ActiveToolType = ToolType.Infantry;
  1039. }
  1040. else if (sender == unitToolStripButton)
  1041. {
  1042. ActiveToolType = ToolType.Unit;
  1043. }
  1044. else if (sender == buildingToolStripButton)
  1045. {
  1046. ActiveToolType = ToolType.Building;
  1047. }
  1048. else if (sender == resourcesToolStripButton)
  1049. {
  1050. ActiveToolType = ToolType.Resources;
  1051. }
  1052. else if (sender == wallsToolStripButton)
  1053. {
  1054. ActiveToolType = ToolType.Wall;
  1055. }
  1056. else if (sender == waypointsToolStripButton)
  1057. {
  1058. ActiveToolType = ToolType.Waypoint;
  1059. }
  1060. else if (sender == cellTriggersToolStripButton)
  1061. {
  1062. ActiveToolType = ToolType.CellTrigger;
  1063. }
  1064. }
  1065. private void UpdateVisibleLayers()
  1066. {
  1067. MapLayerFlag layers = MapLayerFlag.All;
  1068. if (!viewLayersBoundariesMenuItem.Checked)
  1069. {
  1070. layers &= ~MapLayerFlag.Boundaries;
  1071. }
  1072. if (!viewLayersOverlayMenuItem.Checked)
  1073. {
  1074. layers &= ~MapLayerFlag.OverlayAll;
  1075. }
  1076. if (!viewLayersTerrainMenuItem.Checked)
  1077. {
  1078. layers &= ~MapLayerFlag.Terrain;
  1079. }
  1080. if (!viewLayersWaypointsMenuItem.Checked)
  1081. {
  1082. layers &= ~MapLayerFlag.Waypoints;
  1083. }
  1084. if (!viewLayersCellTriggersMenuItem.Checked)
  1085. {
  1086. layers &= ~MapLayerFlag.CellTriggers;
  1087. }
  1088. if (!viewLayersObjectTriggersMenuItem.Checked)
  1089. {
  1090. layers &= ~MapLayerFlag.TechnoTriggers;
  1091. }
  1092. ActiveLayers = layers;
  1093. }
  1094. private void viewLayersMenuItem_CheckedChanged(object sender, EventArgs e)
  1095. {
  1096. UpdateVisibleLayers();
  1097. }
  1098. private void toolTabControl_Selected(object sender, TabControlEventArgs e)
  1099. {
  1100. if (plugin == null)
  1101. {
  1102. return;
  1103. }
  1104. }
  1105. private void developerGenerateMapPreviewMenuItem_Click(object sender, EventArgs e)
  1106. {
  1107. #if DEVELOPER
  1108. if ((plugin == null) || string.IsNullOrEmpty(filename))
  1109. {
  1110. return;
  1111. }
  1112. plugin.Map.GenerateMapPreview().Save(Path.ChangeExtension(filename, ".tga"));
  1113. #endif
  1114. }
  1115. private void developerGoToINIMenuItem_Click(object sender, EventArgs e)
  1116. {
  1117. #if DEVELOPER
  1118. if ((plugin == null) || string.IsNullOrEmpty(filename))
  1119. {
  1120. return;
  1121. }
  1122. var path = Path.ChangeExtension(filename, ".mpr");
  1123. if (!File.Exists(path))
  1124. {
  1125. path = Path.ChangeExtension(filename, ".ini");
  1126. }
  1127. try
  1128. {
  1129. Process.Start(path);
  1130. }
  1131. catch (Win32Exception)
  1132. {
  1133. Process.Start("notepad.exe", path);
  1134. }
  1135. catch (Exception) { }
  1136. #endif
  1137. }
  1138. private void developerGenerateMapPreviewDirectoryMenuItem_Click(object sender, EventArgs e)
  1139. {
  1140. #if DEVELOPER
  1141. FolderBrowserDialog fbd = new FolderBrowserDialog
  1142. {
  1143. ShowNewFolderButton = false
  1144. };
  1145. if (fbd.ShowDialog() == DialogResult.OK)
  1146. {
  1147. var extensions = new string[] { ".ini", ".mpr" };
  1148. foreach (var file in Directory.EnumerateFiles(fbd.SelectedPath).Where(file => extensions.Contains(Path.GetExtension(file).ToLower())))
  1149. {
  1150. GameType gameType = GameType.None;
  1151. var ini = new INI();
  1152. using (var reader = new StreamReader(file))
  1153. {
  1154. ini.Parse(reader);
  1155. }
  1156. gameType = ini.Sections.Contains("MapPack") ? GameType.RedAlert : GameType.TiberianDawn;
  1157. if (gameType == GameType.None)
  1158. {
  1159. continue;
  1160. }
  1161. IGamePlugin plugin = null;
  1162. switch (gameType)
  1163. {
  1164. case GameType.TiberianDawn:
  1165. {
  1166. plugin = new TiberianDawn.GamePlugin(false);
  1167. }
  1168. break;
  1169. case GameType.RedAlert:
  1170. {
  1171. plugin = new RedAlert.GamePlugin(false);
  1172. }
  1173. break;
  1174. }
  1175. plugin.Load(file, FileType.INI);
  1176. plugin.Map.GenerateMapPreview().Save(Path.ChangeExtension(file, ".tga"));
  1177. plugin.Dispose();
  1178. }
  1179. }
  1180. #endif
  1181. }
  1182. private void developerDebugShowOverlapCellsMenuItem_CheckedChanged(object sender, EventArgs e)
  1183. {
  1184. #if DEVELOPER
  1185. Globals.Developer.ShowOverlapCells = developerDebugShowOverlapCellsMenuItem.Checked;
  1186. #endif
  1187. }
  1188. private void filePublishMenuItem_Click(object sender, EventArgs e)
  1189. {
  1190. if (plugin == null)
  1191. {
  1192. return;
  1193. }
  1194. if (!PromptSaveMap())
  1195. {
  1196. return;
  1197. }
  1198. if (plugin.Dirty)
  1199. {
  1200. MessageBox.Show("Map must be saved before publishing.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  1201. return;
  1202. }
  1203. if (new FileInfo(filename).Length > Globals.MaxMapSize)
  1204. {
  1205. return;
  1206. }
  1207. using (var sd = new SteamDialog(plugin))
  1208. {
  1209. sd.ShowDialog();
  1210. }
  1211. fileSaveMenuItem.PerformClick();
  1212. }
  1213. private void mainToolStrip_MouseMove(object sender, MouseEventArgs e)
  1214. {
  1215. mainToolStrip.Focus();
  1216. }
  1217. private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  1218. {
  1219. e.Cancel = !PromptSaveMap();
  1220. }
  1221. private bool PromptSaveMap()
  1222. {
  1223. bool cancel = false;
  1224. if (plugin?.Dirty ?? false)
  1225. {
  1226. var message = string.IsNullOrEmpty(filename) ? "Save new map?" : string.Format("Save map '{0}'?", filename);
  1227. var result = MessageBox.Show(message, "Save", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
  1228. switch (result)
  1229. {
  1230. case DialogResult.Yes:
  1231. {
  1232. if (string.IsNullOrEmpty(filename))
  1233. {
  1234. fileSaveAsMenuItem.PerformClick();
  1235. }
  1236. else
  1237. {
  1238. fileSaveMenuItem.PerformClick();
  1239. }
  1240. }
  1241. break;
  1242. case DialogResult.No:
  1243. break;
  1244. case DialogResult.Cancel:
  1245. cancel = true;
  1246. break;
  1247. }
  1248. }
  1249. return !cancel;
  1250. }
  1251. }
  1252. }