PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/OpenRA.Editor/Form1.cs

https://github.com/mart0258/OpenRA
C# | 490 lines | 381 code | 93 blank | 16 comment | 55 complexity | 089690c494f8c41e2c3593ba586da0df MD5 | raw file
  1. #region Copyright & License Information
  2. /*
  3. * Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
  4. * This file is part of OpenRA, which is free software. It is made
  5. * available to you under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation. For more information,
  7. * see COPYING.
  8. */
  9. #endregion
  10. using System;
  11. using System.Collections.Generic;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Windows.Forms;
  15. using OpenRA.FileFormats;
  16. using OpenRA.Graphics;
  17. using OpenRA.Traits;
  18. using System.Drawing;
  19. namespace OpenRA.Editor
  20. {
  21. public partial class Form1 : Form
  22. {
  23. public Form1(string[] args)
  24. {
  25. InitializeComponent();
  26. AppDomain.CurrentDomain.AssemblyResolve += FileSystem.ResolveAssembly;
  27. currentMod = args.FirstOrDefault() ?? "ra";
  28. toolStripComboBox1.Items.AddRange(Mod.AllMods.Keys.ToArray());
  29. toolStripComboBox1.SelectedIndexChanged += (_, e) =>
  30. {
  31. tilePalette.SuspendLayout();
  32. actorPalette.SuspendLayout();
  33. resourcePalette.SuspendLayout();
  34. tilePalette.Controls.Clear();
  35. actorPalette.Controls.Clear();
  36. resourcePalette.Controls.Clear();
  37. tilePalette.ResumeLayout();
  38. actorPalette.ResumeLayout();
  39. resourcePalette.ResumeLayout();
  40. surface1.Bind(null, null, null);
  41. pmMiniMap.Image = null;
  42. currentMod = toolStripComboBox1.SelectedItem as string;
  43. Text = "OpenRA Editor (mod:{0})".F(currentMod);
  44. Game.modData = new ModData(currentMod);
  45. FileSystem.LoadFromManifest(Game.modData.Manifest);
  46. Rules.LoadRules(Game.modData.Manifest, new Map());
  47. loadedMapName = null;
  48. };
  49. toolStripComboBox1.SelectedItem = currentMod;
  50. surface1.AfterChange += OnMapChanged;
  51. surface1.MousePositionChanged += s => toolStripStatusLabelMousePosition.Text = s;
  52. if (args.Length >= 2)
  53. LoadMap(args[1]);
  54. }
  55. void OnMapChanged()
  56. {
  57. MakeDirty();
  58. pmMiniMap.Image = Minimap.AddStaticResources(surface1.Map, Minimap.TerrainBitmap(surface1.Map, true));
  59. }
  60. void MakeDirty() { dirty = true; }
  61. string loadedMapName;
  62. string currentMod = "ra";
  63. TileSet tileset;
  64. bool dirty = false;
  65. void LoadMap(string mapname)
  66. {
  67. tilePalette.Controls.Clear();
  68. actorPalette.Controls.Clear();
  69. resourcePalette.Controls.Clear();
  70. loadedMapName = mapname;
  71. // load the map
  72. var map = new Map(mapname);
  73. // upgrade maps that have no player definitions. editor doesnt care,
  74. // but this breaks the game pretty badly.
  75. if (map.Players.Count == 0)
  76. map.MakeDefaultPlayers();
  77. PrepareMapResources(Game.modData.Manifest, map);
  78. dirty = false;
  79. }
  80. void NewMap(Map map)
  81. {
  82. tilePalette.Controls.Clear();
  83. actorPalette.Controls.Clear();
  84. resourcePalette.Controls.Clear();
  85. loadedMapName = null;
  86. PrepareMapResources(Game.modData.Manifest, map);
  87. MakeDirty();
  88. }
  89. // this code is insanely stupid, and mostly my fault -- chrisf
  90. void PrepareMapResources(Manifest manifest, Map map)
  91. {
  92. Rules.LoadRules(manifest, map);
  93. tileset = Rules.TileSets[map.Tileset];
  94. tileset.LoadTiles();
  95. var palette = new Palette(FileSystem.Open(tileset.Palette), true);
  96. surface1.Bind(map, tileset, palette);
  97. // construct the palette of tiles
  98. var palettes = new[] { tilePalette, actorPalette, resourcePalette };
  99. foreach (var p in palettes) { p.Visible = false; p.SuspendLayout(); }
  100. foreach (var t in tileset.Templates)
  101. {
  102. try
  103. {
  104. var bitmap = tileset.RenderTemplate((ushort)t.Key, palette);
  105. var ibox = new PictureBox
  106. {
  107. Image = bitmap,
  108. Width = bitmap.Width / 2,
  109. Height = bitmap.Height / 2,
  110. SizeMode = PictureBoxSizeMode.StretchImage
  111. };
  112. var brushTemplate = new BrushTemplate { Bitmap = bitmap, N = t.Key };
  113. ibox.Click += (_, e) => surface1.SetTool(new BrushTool(brushTemplate));
  114. var template = t.Value;
  115. tilePalette.Controls.Add(ibox);
  116. tt.SetToolTip(ibox,
  117. "{1}:{0} ({2}x{3})".F(
  118. template.Image,
  119. template.Id,
  120. template.Size.X,
  121. template.Size.Y));
  122. }
  123. catch { }
  124. }
  125. var actorTemplates = new List<ActorTemplate>();
  126. foreach (var a in Rules.Info.Keys)
  127. {
  128. try
  129. {
  130. var info = Rules.Info[a];
  131. if (!info.Traits.Contains<RenderSimpleInfo>()) continue;
  132. var etf = info.Traits.GetOrDefault<EditorTilesetFilterInfo>();
  133. if (etf != null && etf.ExcludeTilesets != null
  134. && etf.ExcludeTilesets.Contains(tileset.Id)) continue;
  135. if (etf != null && etf.RequireTilesets != null
  136. && !etf.RequireTilesets.Contains(tileset.Id)) continue;
  137. var template = RenderUtils.RenderActor(info, tileset, palette);
  138. var ibox = new PictureBox
  139. {
  140. Image = template.Bitmap,
  141. Width = 32,
  142. Height = 32,
  143. SizeMode = PictureBoxSizeMode.Zoom,
  144. BorderStyle = BorderStyle.FixedSingle
  145. };
  146. ibox.Click += (_, e) => surface1.SetTool(new ActorTool(template));
  147. actorPalette.Controls.Add(ibox);
  148. tt.SetToolTip(ibox,
  149. "{0}".F(
  150. info.Name));
  151. actorTemplates.Add(template);
  152. }
  153. catch { }
  154. }
  155. surface1.BindActorTemplates(actorTemplates);
  156. var resourceTemplates = new List<ResourceTemplate>();
  157. foreach (var a in Rules.Info["world"].Traits.WithInterface<ResourceTypeInfo>())
  158. {
  159. try
  160. {
  161. var template = RenderUtils.RenderResourceType(a, tileset.Extensions, palette);
  162. var ibox = new PictureBox
  163. {
  164. Image = template.Bitmap,
  165. Width = 32,
  166. Height = 32,
  167. SizeMode = PictureBoxSizeMode.Zoom,
  168. BorderStyle = BorderStyle.FixedSingle
  169. };
  170. ibox.Click += (_, e) => surface1.SetTool(new ResourceTool(template));
  171. resourcePalette.Controls.Add(ibox);
  172. tt.SetToolTip(ibox,
  173. "{0}:{1}cr".F(
  174. template.Info.Name,
  175. template.Info.ValuePerUnit));
  176. resourceTemplates.Add(template);
  177. }
  178. catch { }
  179. }
  180. surface1.BindResourceTemplates(resourceTemplates);
  181. foreach (var p in palettes)
  182. {
  183. p.Visible = true;
  184. p.ResumeLayout();
  185. }
  186. pmMiniMap.Image = Minimap.AddStaticResources(surface1.Map, Minimap.TerrainBitmap(surface1.Map, true));
  187. propertiesToolStripMenuItem.Enabled = true;
  188. resizeToolStripMenuItem.Enabled = true;
  189. saveToolStripMenuItem.Enabled = true;
  190. saveAsToolStripMenuItem.Enabled = true;
  191. mnuMinimapToPNG.Enabled = true; // todo: what is this VB naming bullshit doing here?
  192. actorOwnerChooser.Items.Clear();
  193. actorOwnerChooser.Items.AddRange(map.Players.Values.ToArray());
  194. actorOwnerChooser.SelectedIndex = 0;
  195. surface1.NewActorOwner = (actorOwnerChooser.SelectedItem as PlayerReference).Name;
  196. }
  197. void ResizeClicked(object sender, EventArgs e)
  198. {
  199. using (var rd = new ResizeDialog())
  200. {
  201. rd.width.Value = surface1.Map.MapSize.X;
  202. rd.height.Value = surface1.Map.MapSize.Y;
  203. rd.cordonLeft.Value = surface1.Map.Bounds.Left;
  204. rd.cordonTop.Value = surface1.Map.Bounds.Top;
  205. rd.cordonRight.Value = surface1.Map.Bounds.Right;
  206. rd.cordonBottom.Value = surface1.Map.Bounds.Bottom;
  207. if (DialogResult.OK != rd.ShowDialog())
  208. return;
  209. surface1.Map.ResizeCordon((int)rd.cordonLeft.Value,
  210. (int)rd.cordonTop.Value,
  211. (int)rd.cordonRight.Value,
  212. (int)rd.cordonBottom.Value);
  213. if ((int)rd.width.Value != surface1.Map.MapSize.X || (int)rd.height.Value != surface1.Map.MapSize.Y)
  214. {
  215. surface1.Map.Resize((int)rd.width.Value, (int)rd.height.Value);
  216. surface1.Bind(surface1.Map, surface1.TileSet, surface1.Palette); // rebind it to invalidate all caches
  217. }
  218. surface1.Invalidate();
  219. }
  220. }
  221. void SaveClicked(object sender, EventArgs e)
  222. {
  223. if (loadedMapName == null)
  224. SaveAsClicked(sender, e);
  225. else
  226. {
  227. surface1.Map.Save(loadedMapName);
  228. dirty = false;
  229. }
  230. }
  231. void SaveAsClicked(object sender, EventArgs e)
  232. {
  233. using (var nms = new MapSelect(currentMod))
  234. {
  235. nms.txtNew.ReadOnly = false;
  236. nms.btnOk.Text = "Save";
  237. nms.txtNew.Text = "unnamed";
  238. nms.txtPathOut.ReadOnly = false;
  239. if (DialogResult.OK == nms.ShowDialog())
  240. {
  241. if (nms.txtNew.Text == "")
  242. nms.txtNew.Text = "unnamed";
  243. // TODO: Allow the user to choose map format (directory vs oramap)
  244. loadedMapName = Path.Combine(nms.MapFolderPath, nms.txtNew.Text + ".oramap");
  245. SaveClicked(sender, e);
  246. }
  247. }
  248. }
  249. void OpenClicked(object sender, EventArgs e)
  250. {
  251. using (var nms = new MapSelect(currentMod))
  252. {
  253. nms.txtNew.ReadOnly = true;
  254. nms.txtPathOut.ReadOnly = true;
  255. nms.btnOk.Text = "Open";
  256. if (DialogResult.OK == nms.ShowDialog())
  257. LoadMap(nms.txtNew.Tag as string);
  258. }
  259. }
  260. void NewClicked(object sender, EventArgs e)
  261. {
  262. using (var nmd = new NewMapDialog())
  263. {
  264. nmd.theater.Items.Clear();
  265. nmd.theater.Items.AddRange(Rules.TileSets.Select(a => a.Value.Id).ToArray());
  266. nmd.theater.SelectedIndex = 0;
  267. if (DialogResult.OK == nmd.ShowDialog())
  268. {
  269. var map = Map.FromTileset(nmd.theater.SelectedItem as string);
  270. map.Resize((int)nmd.width.Value, (int)nmd.height.Value);
  271. map.ResizeCordon((int)nmd.cordonLeft.Value, (int)nmd.cordonTop.Value,
  272. (int)nmd.cordonRight.Value, (int)nmd.cordonBottom.Value);
  273. map.MakeDefaultPlayers();
  274. NewMap(map);
  275. }
  276. }
  277. }
  278. void PropertiesClicked(object sender, EventArgs e)
  279. {
  280. using (var pd = new PropertiesDialog())
  281. {
  282. pd.title.Text = surface1.Map.Title;
  283. pd.desc.Text = surface1.Map.Description;
  284. pd.author.Text = surface1.Map.Author;
  285. pd.selectable.Checked = surface1.Map.Selectable;
  286. pd.useAsShellmap.Checked = surface1.Map.UseAsShellmap;
  287. if (DialogResult.OK != pd.ShowDialog())
  288. return;
  289. surface1.Map.Title = pd.title.Text;
  290. surface1.Map.Description = pd.desc.Text;
  291. surface1.Map.Author = pd.author.Text;
  292. surface1.Map.Selectable = pd.selectable.Checked;
  293. surface1.Map.UseAsShellmap = pd.useAsShellmap.Checked;
  294. }
  295. }
  296. void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Space) surface1.IsPanning = true; }
  297. void Form1_KeyUp(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Space) surface1.IsPanning = false; }
  298. void CloseClicked(object sender, EventArgs e)
  299. {
  300. Close();
  301. }
  302. void ImportLegacyMapClicked(object sender, EventArgs e)
  303. {
  304. using (var ofd = new OpenFileDialog { RestoreDirectory = true,
  305. Filter = "Legacy maps (*.ini;*.mpr)|*.ini;*.mpr" })
  306. if (DialogResult.OK == ofd.ShowDialog())
  307. {
  308. /* massive hack: we should be able to call NewMap() with the imported Map object,
  309. * but something's not right internally in it, unless loaded via the real maploader */
  310. var savePath = Path.Combine(Path.GetTempPath(), "OpenRA.Import");
  311. Directory.CreateDirectory(savePath);
  312. var errors = new List<string>();
  313. var map = LegacyMapImporter.Import(ofd.FileName, a => errors.Add(a));
  314. if (errors.Count > 0)
  315. using (var eld = new ErrorListDialog(errors))
  316. eld.ShowDialog();
  317. map.MakeDefaultPlayers();
  318. map.Save(savePath);
  319. LoadMap(savePath);
  320. loadedMapName = null; /* editor needs to think this hasnt been saved */
  321. Directory.Delete(savePath, true);
  322. MakeDirty();
  323. }
  324. }
  325. void OnFormClosing(object sender, FormClosingEventArgs e)
  326. {
  327. if (!dirty) return;
  328. switch (MessageBox.Show("The map has been modified since it was last saved. " + "\r\n" + "Save changes now?",
  329. "Unsaved Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation))
  330. {
  331. case DialogResult.Yes: SaveClicked(null, EventArgs.Empty); break;
  332. case DialogResult.No: break;
  333. case DialogResult.Cancel: e.Cancel = true; break;
  334. }
  335. }
  336. void ExportMinimap(object sender, EventArgs e)
  337. {
  338. saveFileDialog.InitialDirectory = Path.Combine(Environment.CurrentDirectory, "maps");
  339. saveFileDialog.FileName = Path.ChangeExtension(loadedMapName, ".png");
  340. if (DialogResult.OK == saveFileDialog.ShowDialog())
  341. pmMiniMap.Image.Save(saveFileDialog.FileName);
  342. }
  343. void ShowActorNamesClicked(object sender, EventArgs e)
  344. {
  345. showActorNamesToolStripMenuItem.Checked ^= true;
  346. surface1.ShowActorNames = showActorNamesToolStripMenuItem.Checked;
  347. }
  348. void ShowGridClicked(object sender, EventArgs e)
  349. {
  350. showGridToolStripMenuItem.Checked ^= true;
  351. surface1.ShowGrid = showGridToolStripMenuItem.Checked;
  352. surface1.Chunks.Clear();
  353. }
  354. void FixOpenAreas(object sender, EventArgs e)
  355. {
  356. dirty = true;
  357. var r = new Random();
  358. for (var j = surface1.Map.Bounds.Top; j < surface1.Map.Bounds.Bottom; j++)
  359. for (var i = surface1.Map.Bounds.Left; i < surface1.Map.Bounds.Right; i++)
  360. {
  361. var tr = surface1.Map.MapTiles.Value[i, j];
  362. if (tr.type == 0xff || tr.type == 0xffff || tr.type == 1 || tr.type == 2)
  363. tr.index = (byte)r.Next(0,
  364. Rules.TileSets[surface1.Map.Tileset].Templates[tr.type].Data.TileBitmapBytes.Count);
  365. surface1.Map.MapTiles.Value[i, j] = tr;
  366. }
  367. surface1.Chunks.Clear();
  368. surface1.Invalidate();
  369. }
  370. void SetupDefaultPlayers(object sender, EventArgs e)
  371. {
  372. dirty = true;
  373. surface1.Map.MakeDefaultPlayers();
  374. surface1.Chunks.Clear();
  375. surface1.Invalidate();
  376. }
  377. void onDrawPlayerItem(object sender, DrawItemEventArgs e)
  378. {
  379. // color block
  380. var player = e.Index >= 0 ? (PlayerReference)(sender as ComboBox).Items[e.Index] : null;
  381. e.DrawBackground();
  382. e.DrawFocusRectangle();
  383. if (player == null)
  384. return;
  385. var color = player.ColorRamp.GetColor(0);
  386. using( var brush = new SolidBrush(color) )
  387. e.Graphics.FillRectangle( brush, e.Bounds.Left + 2, e.Bounds.Top + 2, e.Bounds.Height + 6, e.Bounds.Height - 4 );
  388. using( var foreBrush = new SolidBrush(e.ForeColor) )
  389. e.Graphics.DrawString( player.Name, e.Font, foreBrush, e.Bounds.Left + e.Bounds.Height + 12, e.Bounds.Top );
  390. }
  391. void onSelectOwner(object sender, EventArgs e)
  392. {
  393. var player = actorOwnerChooser.SelectedItem as PlayerReference;
  394. surface1.NewActorOwner = player.Name;
  395. }
  396. }
  397. }