v1.4.3.3 stuff initial commit

This commit is contained in:
Nyeguds 2022-09-19 12:23:44 +02:00
parent 60b6eebf41
commit de53479e32
43 changed files with 1160 additions and 304 deletions

View File

@ -316,6 +316,7 @@
<Compile Include="Model\Infantry.cs" />
<Compile Include="Model\Map.cs" />
<Compile Include="Model\MapSection.cs" />
<Compile Include="Model\NotifiableIniSection.cs" />
<Compile Include="Model\OccupierSet.cs" />
<Compile Include="Model\OverlapperSet.cs" />
<Compile Include="Model\Overlay.cs" />
@ -559,6 +560,7 @@
<Compile Include="Utility\GeneralUtils.cs" />
<Compile Include="Utility\GenericBooleanTypeConverter.cs" />
<Compile Include="Utility\INI.cs" />
<Compile Include="Utility\Keyboard.cs" />
<Compile Include="Utility\ListItem.cs" />
<Compile Include="Utility\Megafile.cs" />
<Compile Include="Utility\MegafileBuilder.cs" />

View File

@ -13,6 +13,7 @@
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using System;
using System.Data;
@ -23,7 +24,7 @@ namespace MobiusEditor.Controls
{
public partial class BasicSettings : UserControl
{
public BasicSettings(IGamePlugin plugin, dynamic basicSection)
public BasicSettings(IGamePlugin plugin, PropertyTracker<BasicSection> basicSection)
{
InitializeComponent();
playerComboBox.DataSource = plugin.Map.Houses.Select(h => h.Type.Name).ToArray();

View File

@ -13,13 +13,15 @@
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using System.Windows.Forms;
namespace MobiusEditor.Controls
{
public partial class BriefingSettings : UserControl
{
public BriefingSettings(IGamePlugin plugin, dynamic briefingSection)
public BriefingSettings(IGamePlugin plugin, PropertyTracker<BriefingSection> briefingSection)
{
InitializeComponent();

View File

@ -269,11 +269,7 @@ namespace MobiusEditor.Controls
}
} break;
}
if (!isMockObject)
{
Plugin.Dirty = true;
}
// The undo/redo system now handles plugin dirty state.
}
private void AdjustToStructurePrebuiltStatus(Building building)

View File

@ -52,12 +52,12 @@ namespace MobiusEditor.Controls
// txtRules
//
this.txtRules.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtRules.Location = new System.Drawing.Point(2, 36);
this.txtRules.Location = new System.Drawing.Point(2, 49);
this.txtRules.Margin = new System.Windows.Forms.Padding(2, 10, 2, 2);
this.txtRules.Multiline = true;
this.txtRules.Name = "txtRules";
this.txtRules.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtRules.Size = new System.Drawing.Size(596, 362);
this.txtRules.Size = new System.Drawing.Size(596, 349);
this.txtRules.TabIndex = 1;
this.txtRules.Leave += new System.EventHandler(this.txtRules_Leave);
//
@ -67,7 +67,7 @@ namespace MobiusEditor.Controls
this.lblRules.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblRules.Location = new System.Drawing.Point(3, 0);
this.lblRules.Name = "lblRules";
this.lblRules.Size = new System.Drawing.Size(594, 26);
this.lblRules.Size = new System.Drawing.Size(594, 39);
this.lblRules.TabIndex = 0;
this.lblRules.Text = resources.GetString("lblRules.Text");
//

View File

@ -118,6 +118,6 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="lblRules.Text" xml:space="preserve">
<value>The text below shows all INI sections not managed by the editor. Add any rule changes you need. Sections managed by the editor ([Basic], [Units], [Trigs] etc.) will be ignored. Rule changes will be applied in the editor</value>
<value>The text below shows all INI sections not managed by the editor. Add any rule changes you need. Sections managed by the editor ([Basic], [Units], [Trigs] etc.) will be ignored. Rule changes will be applied in the editor. [Aftermath] can be added, but its "NewUnitsEnabled" option is managed by the editor (in "Basic") and will be ignored here.</value>
</data>
</root>

View File

@ -13,6 +13,7 @@
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Interface;
using MobiusEditor.Model;
using MobiusEditor.Utility;
using System;
using System.Data;
@ -23,7 +24,7 @@ namespace MobiusEditor.Controls
{
public partial class ScenarioSettings : UserControl
{
public ScenarioSettings(dynamic basicSection)
public ScenarioSettings(PropertyTracker<BasicSection> basicSection)
{
InitializeComponent();
chkToCarryOver.DataBindings.Add("Checked", basicSection, "ToCarryOver");

View File

@ -99,10 +99,7 @@ namespace MobiusEditor.Controls
}
break;
}
if (!isMockObject)
{
Plugin.Dirty = true;
}
// The undo/redo system now handles plugin dirty state.
}
private void comboBox_SelectedValueChanged(object sender, EventArgs e)

View File

@ -116,10 +116,7 @@ namespace MobiusEditor.Controls
}
break;
}
if (!isMockObject)
{
Plugin.Dirty = true;
}
// The undo/redo system now handles plugin dirty state.
}
private void comboBox_SelectedValueChanged(object sender, EventArgs e)

View File

@ -111,7 +111,8 @@ namespace MobiusEditor.Dialogs
foreach (var player in this.plugin.Map.Houses)
{
var playerNode = playersNode.Nodes.Add(player.Type.Name, player.Type.Name);
playerNode.Checked = player.Enabled;
bool enabled = houseSettingsTrackers[player].TryGetMember("Enabled", out object res) && (res is bool en) && en;
playerNode.Checked = enabled;
}
playersNode.Expand();
settingsTreeView.EndUpdate();
@ -158,7 +159,6 @@ namespace MobiusEditor.Dialogs
break;
case "RULES":
{
// TODO clone briefing panel, add extra logic.
RulesSettings rulesPanel = new RulesSettings(ExtraIniText);
rulesPanel.TextNeedsUpdating += this.RulesPanel_TextNeedsUpdating;
settingsPanel.Controls.Add(rulesPanel);
@ -235,7 +235,9 @@ namespace MobiusEditor.Dialogs
var player = plugin.Map.Houses.Where(h => h.Type.Name == e.Node.Name).FirstOrDefault();
if (player != null)
{
((dynamic)houseSettingsTrackers[player]).Enabled = e.Node.Checked;
// I don't think I like "dynamic". With "TrySetMember" you at least SEE that the variable is looked up by string.
//((dynamic)houseSettingsTrackers[player]).Enabled = e.Node.Checked;
houseSettingsTrackers[player].TrySetMember("Enabled", e.Node.Checked);
}
}

View File

@ -13,6 +13,7 @@
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Controls;
using MobiusEditor.Interface;
using MobiusEditor.Model;
namespace MobiusEditor.Event
@ -21,11 +22,21 @@ namespace MobiusEditor.Event
{
public MapPanel MapPanel { get; private set; }
public IGamePlugin Plugin { get; private set; }
public Map Map { get; private set; }
public UndoRedoEventArgs(MapPanel mapPanel, Map map)
public UndoRedoEventArgs(MapPanel mapPanel, IGamePlugin plugin)
{
MapPanel = mapPanel;
Plugin = plugin;
Map = plugin.Map;
}
public UndoRedoEventArgs(MapPanel mapPanel, Map map, IGamePlugin plugin)
{
MapPanel = mapPanel;
Plugin = plugin;
Map = map;
}
}

View File

@ -50,7 +50,7 @@ namespace MobiusEditor.Interface
void New(string theater);
IEnumerable<string> Load(string path, FileType fileType);
IEnumerable<string> Load(string path, FileType fileType, out bool modified);
bool Save(string path, FileType fileType);

View File

@ -5,17 +5,18 @@ namespace MobiusEditor.Interface
[Flags]
public enum ToolType
{
None = 0,
Map = 1 << 0,
Smudge = 1 << 1,
Overlay = 1 << 2,
Terrain = 1 << 3,
Infantry = 1 << 4,
Unit = 1 << 5,
Building = 1 << 6,
Resources = 1 << 7,
Wall = 1 << 8,
Waypoint = 1 << 9,
CellTrigger = 1 << 10
None = 0,
Map = 1 << 0,
Smudge = 1 << 1,
Overlay = 1 << 2,
Terrain = 1 << 3,
Infantry = 1 << 4,
Unit = 1 << 5,
Building = 1 << 6,
Resources = 1 << 7,
Wall = 1 << 8,
Waypoint = 1 << 9,
CellTrigger = 1 << 10,
Select = 1 << 11
}
}

View File

@ -68,6 +68,8 @@ namespace MobiusEditor
this.settingsTriggersMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsPowerMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsStorageMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsRandomizeTilesMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsExportImageMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mapToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
@ -112,8 +114,8 @@ namespace MobiusEditor
this.wallsToolStripButton = new MobiusEditor.Controls.ViewToolStripButton();
this.waypointsToolStripButton = new MobiusEditor.Controls.ViewToolStripButton();
this.cellTriggersToolStripButton = new MobiusEditor.Controls.ViewToolStripButton();
this.selectToolStripButton = new MobiusEditor.Controls.ViewToolStripButton();
this.mapPanel = new MobiusEditor.Controls.MapPanel();
this.toolsStorageMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mainMenuStrip.SuspendLayout();
this.mainStatusStrip.SuspendLayout();
this.mainToolStrip.SuspendLayout();
@ -131,7 +133,7 @@ namespace MobiusEditor
this.developerToolStripMenuItem});
this.mainMenuStrip.Location = new System.Drawing.Point(0, 0);
this.mainMenuStrip.Name = "mainMenuStrip";
this.mainMenuStrip.Size = new System.Drawing.Size(1027, 24);
this.mainMenuStrip.Size = new System.Drawing.Size(1008, 24);
this.mainMenuStrip.TabIndex = 1;
this.mainMenuStrip.Text = "menuStrip1";
//
@ -302,6 +304,7 @@ namespace MobiusEditor
this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolsPowerMenuItem,
this.toolsStorageMenuItem,
this.toolsRandomizeTilesMenuItem,
this.toolsExportImageMenuItem});
this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20);
@ -315,6 +318,20 @@ namespace MobiusEditor
this.toolsPowerMenuItem.Text = "&Power Balance...";
this.toolsPowerMenuItem.Click += new System.EventHandler(this.ToolsPowerMenuItem_Click);
//
// toolsStorageMenuItem
//
this.toolsStorageMenuItem.Name = "toolsStorageMenuItem";
this.toolsStorageMenuItem.Size = new System.Drawing.Size(204, 22);
this.toolsStorageMenuItem.Text = "Silo Storage...";
this.toolsStorageMenuItem.Click += new System.EventHandler(this.ToolsStorageMenuItem_Click);
//
// toolsRandomizeTilesMenuItem
//
this.toolsRandomizeTilesMenuItem.Name = "toolsRandomizeTilesMenuItem";
this.toolsRandomizeTilesMenuItem.Size = new System.Drawing.Size(204, 22);
this.toolsRandomizeTilesMenuItem.Text = "Re-randomize tiles";
this.toolsRandomizeTilesMenuItem.Click += new System.EventHandler(this.ToolsRandomizeTilesMenuItem_Click);
//
// toolsExportImageMenuItem
//
this.toolsExportImageMenuItem.Name = "toolsExportImageMenuItem";
@ -572,10 +589,10 @@ namespace MobiusEditor
this.toolStatusLabel,
this.cellStatusLabel,
this.copyrightStatusLabel});
this.mainStatusStrip.Location = new System.Drawing.Point(0, 527);
this.mainStatusStrip.Location = new System.Drawing.Point(0, 539);
this.mainStatusStrip.Name = "mainStatusStrip";
this.mainStatusStrip.Padding = new System.Windows.Forms.Padding(2, 0, 14, 0);
this.mainStatusStrip.Size = new System.Drawing.Size(1027, 22);
this.mainStatusStrip.Size = new System.Drawing.Size(1008, 22);
this.mainStatusStrip.TabIndex = 2;
this.mainStatusStrip.Text = "statusStrip1";
//
@ -594,7 +611,7 @@ namespace MobiusEditor
// copyrightStatusLabel
//
this.copyrightStatusLabel.Name = "copyrightStatusLabel";
this.copyrightStatusLabel.Size = new System.Drawing.Size(1007, 17);
this.copyrightStatusLabel.Size = new System.Drawing.Size(988, 17);
this.copyrightStatusLabel.Spring = true;
this.copyrightStatusLabel.Text = "©2020 Electronic Arts Inc.";
this.copyrightStatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
@ -613,11 +630,12 @@ namespace MobiusEditor
this.resourcesToolStripButton,
this.wallsToolStripButton,
this.waypointsToolStripButton,
this.cellTriggersToolStripButton});
this.cellTriggersToolStripButton,
this.selectToolStripButton});
this.mainToolStrip.Location = new System.Drawing.Point(0, 24);
this.mainToolStrip.Name = "mainToolStrip";
this.mainToolStrip.Padding = new System.Windows.Forms.Padding(0, 0, 2, 0);
this.mainToolStrip.Size = new System.Drawing.Size(1027, 31);
this.mainToolStrip.Size = new System.Drawing.Size(1008, 31);
this.mainToolStrip.TabIndex = 3;
this.mainToolStrip.Text = "toolStrip1";
this.mainToolStrip.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mainToolStrip_MouseMove);
@ -732,6 +750,16 @@ namespace MobiusEditor
this.cellTriggersToolStripButton.ToolType = MobiusEditor.Interface.ToolType.CellTrigger;
this.cellTriggersToolStripButton.Click += new System.EventHandler(this.mainToolStripButton_Click);
//
// selectToolStripButton
//
this.selectToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("selectToolStripButton.Image")));
this.selectToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.selectToolStripButton.Name = "selectToolStripButton";
this.selectToolStripButton.Size = new System.Drawing.Size(66, 28);
this.selectToolStripButton.Text = "Select";
this.selectToolStripButton.ToolType = MobiusEditor.Interface.ToolType.Select;
this.selectToolStripButton.Visible = false;
//
// mapPanel
//
this.mapPanel.AllowDrop = true;
@ -743,7 +771,7 @@ namespace MobiusEditor
this.mapPanel.MaxZoom = 8D;
this.mapPanel.MinZoom = 1D;
this.mapPanel.Name = "mapPanel";
this.mapPanel.Size = new System.Drawing.Size(1027, 472);
this.mapPanel.Size = new System.Drawing.Size(1008, 484);
this.mapPanel.SmoothScale = false;
this.mapPanel.TabIndex = 4;
this.mapPanel.Zoom = 1D;
@ -752,18 +780,11 @@ namespace MobiusEditor
this.mapPanel.DragEnter += new System.Windows.Forms.DragEventHandler(this.MapPanel_DragEnter);
this.mapPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.MapPanel_MouseMove);
//
// toolsStorageMenuItem
//
this.toolsStorageMenuItem.Name = "toolsStorageMenuItem";
this.toolsStorageMenuItem.Size = new System.Drawing.Size(204, 22);
this.toolsStorageMenuItem.Text = "Silo Storage...";
this.toolsStorageMenuItem.Click += new System.EventHandler(this.ToolsStorageMenuItem_Click);
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1027, 549);
this.ClientSize = new System.Drawing.Size(1008, 561);
this.Controls.Add(this.mapPanel);
this.Controls.Add(this.mainToolStrip);
this.Controls.Add(this.mainStatusStrip);
@ -857,6 +878,8 @@ namespace MobiusEditor
private System.Windows.Forms.ToolStripMenuItem viewIndicatorsCellTriggersMenuItem;
private System.Windows.Forms.ToolStripMenuItem editClearUndoRedoMenuItem;
private System.Windows.Forms.ToolStripMenuItem toolsStorageMenuItem;
private System.Windows.Forms.ToolStripMenuItem toolsRandomizeTilesMenuItem;
private Controls.ViewToolStripButton selectToolStripButton;
}
}

View File

@ -130,7 +130,8 @@ namespace MobiusEditor
resourcesToolStripButton,
wallsToolStripButton,
waypointsToolStripButton,
cellTriggersToolStripButton
cellTriggersToolStripButton,
selectToolStripButton,
};
mru = new MRU("Software\\Petroglyph\\CnCRemasteredEditor", 10, fileRecentFilesMenuItem);
mru.FileSelected += Mru_FileSelected;
@ -152,16 +153,23 @@ namespace MobiusEditor
private void SetTitle()
{
//MessageBox.Show("setting title");
string file = filename;
if (plugin != null && file == null)
{
file = "Untitled" + (plugin.GameType == GameType.TiberianDawn ? ".ini" : ".mpr");
}
const string noname = "Untitled";
String mainTitle = GetProgramVersionTitle();
if (file != null)
if (plugin != null)
{
this.Text = string.Format("{0} - {1}{2}", mainTitle, file, plugin != null && plugin.Dirty ? "*" : String.Empty);
string mapName = plugin.Map.BasicSection.Name;
if (String.IsNullOrEmpty(mapName))
{
if (filename != null)
{
mapName = Path.GetFileName(filename);
}
else
{
mapName = noname;
}
}
this.Text = string.Format("{0} - {1} {2}", mainTitle, mapName, plugin != null && plugin.Dirty ? "*" : String.Empty);
}
else
{
@ -202,60 +210,48 @@ namespace MobiusEditor
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Q)
OemScanCode sc = Keyboard.GetScanCode(msg);
if ((keyData & (Keys.Shift | Keys.Control | Keys.Alt)) == Keys.None)
{
mapToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.W)
{
smudgeToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.E)
{
overlayToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.R)
{
terrainToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.T)
{
infantryToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.Y)
{
unitToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.A)
{
buildingToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.S)
{
resourcesToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.D)
{
wallsToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.F)
{
waypointsToolStripButton.PerformClick();
return true;
}
else if (keyData == Keys.G)
{
cellTriggersToolStripButton.PerformClick();
return true;
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;
}
}
else if (keyData == (Keys.Control | Keys.Z))
{
@ -518,7 +514,7 @@ namespace MobiusEditor
{
if (url.CanUndo)
{
url.Undo(new UndoRedoEventArgs(mapPanel, plugin.Map));
url.Undo(new UndoRedoEventArgs(mapPanel, plugin));
}
}
@ -526,7 +522,7 @@ namespace MobiusEditor
{
if (url.CanRedo)
{
url.Redo(new UndoRedoEventArgs(mapPanel, plugin.Map));
url.Redo(new UndoRedoEventArgs(mapPanel, plugin));
}
}
@ -546,27 +542,31 @@ namespace MobiusEditor
}
bool expansionEnabled = plugin.Map.BasicSection.ExpansionEnabled;
bool rulesChanged = false;
var basicSettings = new PropertyTracker<BasicSection>(plugin.Map.BasicSection);
var briefingSettings = new PropertyTracker<BriefingSection>(plugin.Map.BriefingSection);
PropertyTracker<BasicSection> basicSettings = new PropertyTracker<BasicSection>(plugin.Map.BasicSection);
PropertyTracker<BriefingSection> briefingSettings = new PropertyTracker<BriefingSection>(plugin.Map.BriefingSection);
string extraIniText = (plugin.GameType == GameType.RedAlert ? plugin.ExtraIniText : String.Empty).Trim();
var houseSettingsTrackers = plugin.Map.Houses.ToDictionary(h => h, h => new PropertyTracker<House>(h));
Dictionary<House, PropertyTracker<House>> houseSettingsTrackers = plugin.Map.Houses.ToDictionary(h => h, h => new PropertyTracker<House>(h));
using (MapSettingsDialog msd = new MapSettingsDialog(plugin, basicSettings, briefingSettings, houseSettingsTrackers, extraIniText))
{
msd.StartPosition = FormStartPosition.CenterParent;
if (msd.ShowDialog(this) == DialogResult.OK)
{
bool hasChanges = basicSettings.HasChanges || briefingSettings.HasChanges;
basicSettings.Commit();
briefingSettings.Commit();
foreach (var houseSettingsTracker in houseSettingsTrackers.Values)
{
if (houseSettingsTracker.HasChanges)
hasChanges = true;
houseSettingsTracker.Commit();
}
if (plugin.GameType == GameType.RedAlert && !extraIniText.Equals(msd.ExtraIniText, StringComparison.InvariantCultureIgnoreCase))
{
plugin.ExtraIniText = msd.ExtraIniText;
rulesChanged = true;
hasChanges = true;
}
plugin.Dirty = true;
plugin.Dirty = hasChanges;
}
}
if (rulesChanged || (expansionEnabled && !plugin.Map.BasicSection.ExpansionEnabled))
@ -679,6 +679,15 @@ namespace MobiusEditor
}
}
private void ToolsRandomizeTilesMenuItem_Click(Object sender, EventArgs e)
{
if (plugin != null)
{
String feedback = TemplateTool.RandomizeTiles(plugin, mapPanel, url);
MessageBox.Show(feedback, GetProgramVersionTitle());
}
}
private void ToolsExportImage_Click(Object sender, EventArgs e)
{
if (plugin == null)
@ -761,10 +770,11 @@ namespace MobiusEditor
(mapPoint.X * Globals.PixelWidth / Globals.MapTileWidth) % Globals.PixelWidth,
(mapPoint.Y * Globals.PixelHeight / Globals.MapTileHeight) % Globals.PixelHeight
);
var i = InfantryGroup.ClosestStoppingTypes(subPixel).Cast<int>().First();
if (infantryGroup.Infantry[i] != null)
InfantryStoppingType i = InfantryGroup.ClosestStoppingTypes(subPixel).First();
Infantry inf = infantryGroup.Infantry[(int)i];
if (inf != null)
{
sb.AppendFormat(", Infantry = {0}", infantryGroup.Infantry[i].Type.DisplayName);
sb.AppendFormat(", Infantry = {0} ({1})", inf.Type.DisplayName, InfantryGroup.GetStoppingTypeName(i));
}
}
var unit = plugin.Map.Technos[location] as Unit;
@ -938,9 +948,12 @@ namespace MobiusEditor
break;
}
string[] errors;
bool modifiedByLoad;
try
{
errors = plugin.Load(loadFilename, fileType).ToArray();
// 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();
LoadIcons(plugin);
if (errors.Length > 0)
{
@ -967,11 +980,12 @@ namespace MobiusEditor
mapPanel.MapImage = plugin.MapImage;
filename = loadFilename;
loadedFileType = fileType;
plugin.Dirty = errors != null && errors.Length > 0;
plugin.Dirty = modifiedByLoad;
url.Clear();
ClearAllTools();
RefreshAvailableTools();
RefreshActiveTool();
SetTitle();
return true;
}
@ -1097,6 +1111,7 @@ namespace MobiusEditor
if (plugin.Map.OverlayTypes.Any(t => t.IsWall && (!Globals.FilterTheaterObjects || t.Theaters == null || t.Theaters.Contains(th)))) availableToolTypes |= ToolType.Wall;
// Always allow celltrigger tool, even if triggers list is empty; it contains a tooltip saying which triggers are eligible.
availableToolTypes |= ToolType.CellTrigger;
availableToolTypes |= ToolType.Select;
}
foreach (var toolStripButton in viewToolStripButtons)
{
@ -1216,6 +1231,11 @@ namespace MobiusEditor
toolDialog = new CellTriggersToolDialog(this);
}
break;
case ToolType.Select:
{
toolDialog = null; // new SelectToolDialog(this);
}
break;
}
if (toolDialog != null)
{
@ -1231,6 +1251,7 @@ namespace MobiusEditor
if (toolDialog != null)
{
activeToolForm = (Form)toolDialog;
// Creates the actual Tool class
toolDialog.Initialize(mapPanel, active, toolStatusLabel, mouseToolTip, plugin, url);
activeTool = toolDialog.GetTool();
activeToolForm.ResizeEnd -= ActiveToolForm_ResizeEnd;
@ -1630,7 +1651,10 @@ namespace MobiusEditor
private void mainToolStrip_MouseMove(object sender, MouseEventArgs e)
{
mainToolStrip.Focus();
if (Form.ActiveForm != null)
{
mainToolStrip.Focus();
}
}
private void MainForm_Shown(object sender, System.EventArgs e)
@ -1757,9 +1781,20 @@ namespace MobiusEditor
LoadNewIcon(wallsToolStripButton, wall?.Thumbnail, plugin, 8);
LoadNewIcon(waypointsToolStripButton, waypoint?.Image, plugin, 9);
LoadNewIcon(cellTriggersToolStripButton, cellTrigger?.Image, plugin, 10);
// 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);
}
}
private void LoadNewIcon(ViewToolStripButton button, Bitmap image, IGamePlugin plugin, int index)
{
LoadNewIcon(button, image, plugin, index, true);
}
private void LoadNewIcon(ViewToolStripButton button, Bitmap image, IGamePlugin plugin, int index)
private void LoadNewIcon(ViewToolStripButton button, Bitmap image, IGamePlugin plugin, int index, bool crop)
{
if (image == null || plugin == null)
{
@ -1781,7 +1816,7 @@ namespace MobiusEditor
}
else
{
Rectangle opaqueBounds = TextureManager.CalculateOpaqueBounds(image);
Rectangle opaqueBounds = crop ? TextureManager.CalculateOpaqueBounds(image) : new Rectangle(0, 0, image.Width, image.Height);
Bitmap img = image.FitToBoundingBox(opaqueBounds, 24, 24, Color.Transparent);
theaterIcons[id] = img;
button.Image = img;

View File

@ -293,6 +293,21 @@
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
</value>
</data>
<data name="selectToolStripButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
</value>
</data>
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">

View File

@ -14,15 +14,26 @@
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using MobiusEditor.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
namespace MobiusEditor.Model
{
public class PercentageTypeConverter : TypeConverter
{
bool addPercentageSign;
public PercentageTypeConverter()
{
addPercentageSign = false;
}
public PercentageTypeConverter(bool withSign)
{
addPercentageSign = withSign;
}
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (context is MapContext) && (sourceType == typeof(string));
@ -39,9 +50,11 @@ namespace MobiusEditor.Model
{
return null;
}
var mapContext = context as MapContext;
return mapContext.FractionalPercentages ? (percent / 100M).ToString("D2") : percent.ToString();
string retVal = mapContext != null && mapContext.FractionalPercentages ? (percent / 100M).ToString("D2") : percent.ToString();
if (addPercentageSign)
retVal += "%";
return retVal;
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
@ -50,9 +63,11 @@ namespace MobiusEditor.Model
{
return null;
}
str = str.Trim();
var mapContext = context as MapContext;
if (mapContext.FractionalPercentages && str.Contains("."))
if (str.EndsWith("%"))
str = str.Substring(0, str.Length - 1);
if (mapContext != null && mapContext.FractionalPercentages && str.Contains("."))
{
if (!decimal.TryParse(str, out decimal percent))
{
@ -71,10 +86,8 @@ namespace MobiusEditor.Model
}
}
public class BasicSection : INotifyPropertyChanged
public class BasicSection : NotifiableIniSection
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
[DefaultValue(null)]
public string Name { get => name; set => SetField(ref name, value); }
@ -151,18 +164,5 @@ namespace MobiusEditor.Model
private bool soloMission;
[DefaultValue(false)]
public bool SoloMission { get => soloMission; set => SetField(ref soloMission, value); }
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View File

@ -22,7 +22,7 @@ using System.Runtime.CompilerServices;
namespace MobiusEditor.Model
{
public class Building : ITechno, ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable
public class Building : ITechno, ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable, IEquatable<Building>
{
public event PropertyChangedEventHandler PropertyChanged;
@ -137,5 +137,18 @@ namespace MobiusEditor.Model
{
return Clone();
}
public Boolean Equals(Building other)
{
return this.Type == other.Type &&
this.House == other.House &&
this.Strength == other.Strength &&
this.Direction == other.Direction &&
this.Trigger == other.Trigger &&
this.BasePriority == other.BasePriority &&
this.IsPrebuilt == other.IsPrebuilt &&
this.Sellable == other.Sellable &&
this.Rebuild == other.Rebuild;
}
}
}

View File

@ -32,7 +32,7 @@ namespace MobiusEditor.Model
LowerRight = 4
}
public class Infantry : ITechno, INotifyPropertyChanged, ICloneable
public class Infantry : ITechno, INotifyPropertyChanged, ICloneable, IEquatable<Infantry>
{
public event PropertyChangedEventHandler PropertyChanged;
@ -97,10 +97,39 @@ namespace MobiusEditor.Model
{
return Clone();
}
public Boolean Equals(Infantry other)
{
return this.Type == other.Type &&
this.House == other.House &&
this.Strength == other.Strength &&
this.Direction == other.Direction &&
this.Trigger == other.Trigger &&
this.Mission == other.Mission;
}
}
public class InfantryGroup : ICellOverlapper, ICellOccupier
{
private static readonly string[] StoppingTypeNames =
{
"Center",
"Top left",
"Top right",
"Bottom left",
"Bottom right"
};
public static String GetStoppingTypeName(InfantryStoppingType stopLocation)
{
int index = (int)stopLocation;
if (index >= 0 && index < Enum.GetValues(typeof(InfantryStoppingType)).Length)
{
return StoppingTypeNames[index];
}
return null;
}
private static readonly Point[] stoppingLocations = new Point[Globals.NumInfantryStops];
public Rectangle OverlapBounds => new Rectangle(-1, -1, 3, 3);

View File

@ -87,7 +87,7 @@ namespace MobiusEditor.Model
private IDictionary<MapLayerFlag, ISet<Point>> invalidateLayers = new Dictionary<MapLayerFlag, ISet<Point>>();
private bool invalidateOverlappers;
public event EventHandler<MapRefreshEventArgs> RulesChanged;
public void NotifyRulesChanges(ISet<Point> refreshPoints)
{
if (RulesChanged != null)
@ -96,7 +96,14 @@ namespace MobiusEditor.Model
}
}
public event EventHandler<MapRefreshEventArgs> RulesChanged;
public event EventHandler<MapRefreshEventArgs> MapContentsChanged;
public void NotifyMapContentsChanged(ISet<Point> refreshPoints)
{
if (MapContentsChanged != null)
{
this.MapContentsChanged(this, new MapRefreshEventArgs(refreshPoints));
}
}
public readonly BasicSection BasicSection;

View File

@ -13,12 +13,10 @@
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
namespace MobiusEditor.Model
{
@ -56,7 +54,7 @@ namespace MobiusEditor.Model
}
}
public class MapSection : INotifyPropertyChanged
public class MapSection : NotifiableIniSection
{
private readonly int fullWidth;
private readonly int fullHeight;
@ -86,8 +84,6 @@ namespace MobiusEditor.Model
Height = fixedHeight;
}
public event PropertyChangedEventHandler PropertyChanged;
private int x;
[DefaultValue(1)]
public int X
@ -140,18 +136,5 @@ namespace MobiusEditor.Model
[TypeConverter(typeof(TheaterTypeConverter))]
[DefaultValue(null)]
public TheaterType Theater { get => theater; set => SetField(ref theater, value); }
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MobiusEditor.Model
{
public abstract class NotifiableIniSection : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View File

@ -21,7 +21,7 @@ using System.Runtime.CompilerServices;
namespace MobiusEditor.Model
{
public class Smudge: ICellOverlapper, INotifyPropertyChanged, ICloneable
public class Smudge: ICellOverlapper, INotifyPropertyChanged, ICloneable, IEquatable<Smudge>
{
public event PropertyChangedEventHandler PropertyChanged;
private SmudgeType type;
@ -65,6 +65,10 @@ namespace MobiusEditor.Model
Icon = other.Icon;
Tint = other.Tint;
}
public Boolean Equals(Smudge other)
{
return this.Type == other.Type && this.Icon == other.Icon;
}
}
}

View File

@ -21,16 +21,13 @@ using MobiusEditor.Interface;
namespace MobiusEditor.Model
{
public class Terrain : ITechno, ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable
public class Terrain : ITechno, ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable, IEquatable<Terrain>
{
public event PropertyChangedEventHandler PropertyChanged;
private TerrainType type;
public TerrainType Type { get => type; set => SetField(ref type, value); }
private int icon;
public int Icon { get => icon; set => SetField(ref icon, value); }
public Rectangle OverlapBounds => Type.OverlapBounds;
public bool[,] OccupyMask => Type.OccupyMask;
@ -56,7 +53,6 @@ namespace MobiusEditor.Model
public void CloneDataFrom(Terrain other)
{
Type = other.Type;
Icon = other.Icon;
House = other.House;
Strength = other.Strength;
Trigger = other.Trigger;
@ -79,5 +75,13 @@ namespace MobiusEditor.Model
{
return Clone();
}
public Boolean Equals(Terrain other)
{
return this.Type == other.Type &&
this.House == other.House &&
this.Strength == other.Strength &&
this.Trigger == other.Trigger;
}
}
}

View File

@ -21,7 +21,7 @@ using System.Runtime.CompilerServices;
namespace MobiusEditor.Model
{
public class Unit : ITechno, ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable
public class Unit : ITechno, ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable, IEquatable<Unit>
{
public event PropertyChangedEventHandler PropertyChanged;
@ -83,5 +83,15 @@ namespace MobiusEditor.Model
{
return Clone();
}
public Boolean Equals(Unit other)
{
return this.Type == other.Type &&
this.House == other.House &&
this.Strength == other.Strength &&
this.Direction == other.Direction &&
this.Mission == other.Mission &&
this.Trigger == other.Trigger;
}
}
}

View File

@ -293,10 +293,15 @@ namespace MobiusEditor.RedAlert
{
return;
}
// Strip "NewUnitsEnabled" from the Aftermath section.
INISection amSection = ini.Sections["Aftermath"];
if (amSection != null)
{
amSection.Keys.Remove("NewUnitsEnabled");
}
// Remove any sections known and handled / disallowed by the editor.
ini.Sections.Remove("Digest");
ini.Sections.Remove("Basic");
ini.Sections.Remove("Aftermath");
ini.Sections.Remove("Map");
ini.Sections.Remove("Steam");
ini.Sections.Remove("TeamTypes");
@ -417,12 +422,14 @@ namespace MobiusEditor.RedAlert
}
}
public IEnumerable<string> Load(string path, FileType fileType)
public IEnumerable<string> Load(string path, FileType fileType, out bool modified)
{
try
{
isLoading = true;
var errors = new List<string>();
modified = false;
bool forceSingle = false;
switch (fileType)
{
case FileType.INI:
@ -433,7 +440,7 @@ namespace MobiusEditor.RedAlert
{
ini.Parse(reader);
}
bool forceSingle = SinglePlayRegex.IsMatch(Path.GetFileNameWithoutExtension(path));
forceSingle = SinglePlayRegex.IsMatch(Path.GetFileNameWithoutExtension(path));
errors.AddRange(LoadINI(ini, forceSingle));
}
break;
@ -458,6 +465,11 @@ namespace MobiusEditor.RedAlert
default:
throw new NotSupportedException();
}
if (errors.Count > 0)
{
// If only one message is inside the errors, and the "force single" boolean is set, the single message is about that.
modified = !forceSingle || errors.Count > 1;
}
return errors;
}
finally
@ -500,17 +512,16 @@ namespace MobiusEditor.RedAlert
Map.BasicSection.Player = Map.HouseTypes.Where(t => t.Equals(Map.BasicSection.Player)).FirstOrDefault()?.Name ?? Map.HouseTypes.First().Name;
Map.BasicSection.BasePlayer = HouseTypes.GetBasePlayer(Map.BasicSection.Player);
bool aftermathEnabled = false;
var AftermathSection = ini.Sections.Extract("Aftermath");
if (AftermathSection != null)
// Don't remove from extra sections.
INISection aftermathSection = ini.Sections["Aftermath"];
if (aftermathSection != null)
{
foreach (var (Key, Value) in AftermathSection)
{
if ("NewUnitsEnabled".Equals(Key, StringComparison.InvariantCultureIgnoreCase))
{
aftermathEnabled = Int32.TryParse(Value, out int val) && val == 1;
break;
}
}
var amEnabled = aftermathSection["NewUnitsEnabled"];
aftermathEnabled = amEnabled != null && Int32.TryParse(amEnabled, out int val) && val == 1;
aftermathSection.Keys.Remove("NewUnitsEnabled");
// Remove if empty.
if (aftermathSection.Empty)
ini.Sections.Remove(aftermathSection.Name);
}
// Needs to be enabled in advance; it determines which units are valid to have placed on the map.
Map.BasicSection.ExpansionEnabled = aftermathEnabled;
@ -863,7 +874,6 @@ namespace MobiusEditor.RedAlert
Terrain newTerr = new Terrain
{
Type = terrainType,
Icon = terrainType.DisplayIcon,
Trigger = Trigger.None
};
if (Map.Technos.Add(cell, newTerr))
@ -1963,11 +1973,37 @@ namespace MobiusEditor.RedAlert
private void SaveINI(INI ini, FileType fileType, string fileName)
{
INISection aftermathSection = null;
List<INISection> addedExtra = new List<INISection>();
if (extraSections != null)
{
ini.Sections.AddRange(extraSections);
foreach (INISection section in extraSections)
{
if ("Aftermath".Equals(section.Name, StringComparison.OrdinalIgnoreCase))
{
aftermathSection = section;
}
else
{
addedExtra.Add(section);
}
}
}
Model.BasicSection basic = Map.BasicSection;
// Make new section
INISection newAftermathSection = new INISection("Aftermath");
newAftermathSection["NewUnitsEnabled"] = basic.ExpansionEnabled ? "1" : "0";
if (aftermathSection != null)
{
// If old section is present, remove NewUnitsEnabled value from it, and copy the remainder into the new one.
aftermathSection.Keys.Remove("NewUnitsEnabled");
foreach ((string key, string value) in aftermathSection)
{
newAftermathSection[key] = value;
}
}
ini.Sections.Add(newAftermathSection);
ini.Sections.AddRange(addedExtra);
char[] cutfrom = { ';', '(' };
basic.Intro = GeneralUtils.TrimRemarks(basic.Intro, true, cutfrom);
basic.Brief = GeneralUtils.TrimRemarks(basic.Brief, true, cutfrom);
@ -1999,8 +2035,6 @@ namespace MobiusEditor.RedAlert
INI.WriteSection(new MapContext(Map, false), ini.Sections.Add("Steam"), Map.SteamSection);
}
ini["Basic"]["NewINIFormat"] = "3";
var aftermathSection = ini.Sections.Add("Aftermath");
aftermathSection["NewUnitsEnabled"] = basic.ExpansionEnabled ? "1" : "0";
var smudgeSection = ini.Sections.Add("SMUDGE");
// Flatten multi-cell bibs
Dictionary<int, Smudge> resolvedSmudge = new Dictionary<int, Smudge>();

View File

@ -364,9 +364,9 @@ namespace MobiusEditor.Render
public static (Rectangle, Action<Graphics>) Render(GameType gameType, TheaterType theater, Point topLeft, Size tileSize, double tileScale, Terrain terrain)
{
string tileName = terrain.Type.GraphicsSource;
if (!Globals.TheTilesetManager.GetTileData(theater.Tilesets, tileName, terrain.Icon, out Tile tile))
if (!Globals.TheTilesetManager.GetTileData(theater.Tilesets, tileName, terrain.Type.DisplayIcon, out Tile tile))
{
Debug.Print(string.Format("Terrain {0} ({1}) not found", tileName, terrain.Icon));
Debug.Print(string.Format("Terrain {0} ({1}) not found", tileName, terrain.Type.DisplayIcon));
return (Rectangle.Empty, (g) => { });
}
var tint = terrain.Tint;

View File

@ -249,12 +249,14 @@ namespace MobiusEditor.TiberianDawn
UpdateBasePlayerHouse();
}
public IEnumerable<string> Load(string path, FileType fileType)
public IEnumerable<string> Load(string path, FileType fileType, out bool modified)
{
var ini = new INI();
var errors = new List<string>();
var iniPath = fileType == FileType.INI ? path : Path.ChangeExtension(path, ".ini");
var binPath = fileType == FileType.BIN ? path : Path.ChangeExtension(path, ".bin");
modified = false;
bool forceSingle = false;
switch (fileType)
{
case FileType.INI:
@ -264,7 +266,7 @@ namespace MobiusEditor.TiberianDawn
{
string iniText = FixRoad2Load(iniReader);
ini.Parse(iniText);
bool forceSingle = SinglePlayRegex.IsMatch(Path.GetFileNameWithoutExtension(path));
forceSingle = SinglePlayRegex.IsMatch(Path.GetFileNameWithoutExtension(path));
errors.AddRange(LoadINI(ini, forceSingle));
if (binReader.BaseStream.Length != 0x2000)
{
@ -306,6 +308,11 @@ namespace MobiusEditor.TiberianDawn
default:
throw new NotSupportedException();
}
if (errors.Count > 0)
{
// If only one message is inside the errors, and the "force single" boolean is set, the single message is about that.
modified = !forceSingle || errors.Count > 1;
}
return errors;
}
@ -625,7 +632,6 @@ namespace MobiusEditor.TiberianDawn
Terrain newTerr = new Terrain
{
Type = terrainType,
Icon = terrainType.DisplayIcon,
Trigger = tokens[1]
};
if (Map.Technos.Add(cell, newTerr))

View File

@ -118,7 +118,7 @@ namespace MobiusEditor.Tools
selectedObjectProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
AddUndoRedo(building, preEdit);
AddPropertiesUndoRedo(building, preEdit);
};
building.PropertyChanged += SelectedBuilding_PropertyChanged;
selectedObjectProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
@ -127,11 +127,17 @@ namespace MobiusEditor.Tools
}
}
private void AddUndoRedo(Building building, Building preEdit)
private void AddPropertiesUndoRedo(Building building, Building preEdit)
{
// building = building in its final edited form. Clone for preservation
Building redoBuilding = building.Clone();
Building undoBuilding = preEdit;
if (redoBuilding.Equals(undoBuilding))
{
return;
}
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
building.CloneDataFrom(undoBuilding);
@ -141,6 +147,10 @@ namespace MobiusEditor.Tools
building.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, building);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -151,6 +161,10 @@ namespace MobiusEditor.Tools
building.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, building);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -249,6 +263,8 @@ namespace MobiusEditor.Tools
Dictionary<Point, Smudge> eaten = selectedBuildingEatenSmudge.ToDictionary(p => p.Key, p => p.Value);
if (finalLocation.HasValue && finalLocation.Value != selectedBuildingLocation)
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
Point endLocation = finalLocation.Value;
void undoAction(UndoRedoEventArgs ev)
{
@ -268,6 +284,10 @@ namespace MobiusEditor.Tools
}
ev.Map.Buildings.Add(startLocation, toMove);
ev.MapPanel.Invalidate(ev.Map, toMove);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -287,6 +307,10 @@ namespace MobiusEditor.Tools
}
ev.Map.Buildings.Add(endLocation, toMove);
ev.MapPanel.Invalidate(ev.Map, toMove);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -359,7 +383,6 @@ namespace MobiusEditor.Tools
if (map.Technos.CanAdd(newLocation, toMove, toMove.Type.BaseOccupyMask) && map.Buildings.Add(newLocation, toMove))
{
mapPanel.Invalidate(map, toMove);
plugin.Dirty = true;
}
else
{
@ -427,6 +450,8 @@ namespace MobiusEditor.Tools
}
}
mapPanel.Invalidate(map, building);
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, building);
@ -455,6 +480,10 @@ namespace MobiusEditor.Tools
}
}
}
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
@ -469,9 +498,12 @@ namespace MobiusEditor.Tools
}
}
e.MapPanel.Invalidate(e.Map, building);
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
@ -501,6 +533,8 @@ namespace MobiusEditor.Tools
mapPanel.Invalidate(map, baseBuilding);
}
}
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs e)
{
e.Map.Buildings.Add(actualPoint, building);
@ -514,6 +548,10 @@ namespace MobiusEditor.Tools
}
}
e.MapPanel.Invalidate(e.Map, building);
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
@ -529,9 +567,12 @@ namespace MobiusEditor.Tools
e.MapPanel.Invalidate(map, bld);
}
}
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}

View File

@ -114,6 +114,37 @@ namespace MobiusEditor.Tools
{
EnterPlacementMode();
}
else if (triggerComboBox.Enabled)
{
int maxVal = triggerComboBox.Items.Count - 1;
int curVal = triggerComboBox.SelectedIndex;
int newVal = curVal;
switch (e.KeyCode)
{
case Keys.Home:
newVal = 0;
break;
case Keys.End:
newVal = maxVal;
break;
case Keys.PageDown:
newVal = Math.Min(curVal + 10, maxVal);
break;
case Keys.PageUp:
newVal = Math.Max(curVal - 10, 0);
break;
case Keys.Down:
newVal = Math.Min(curVal + 1, maxVal);
break;
case Keys.Up:
newVal = Math.Max(curVal - 1, 0);
break;
}
if (curVal != newVal)
{
triggerComboBox.SelectedIndex = newVal;
}
}
}
private void CellTriggersTool_KeyUp(object sender, KeyEventArgs e)
@ -173,7 +204,6 @@ namespace MobiusEditor.Tools
map.CellTriggers[cell] = cellTrigger;
redoCellTriggers[cell] = cellTrigger;
mapPanel.Invalidate();
plugin.Dirty = true;
}
}
}
@ -193,7 +223,6 @@ namespace MobiusEditor.Tools
map.CellTriggers[cell] = null;
redoCellTriggers[cell] = null;
mapPanel.Invalidate();
plugin.Dirty = true;
}
}
}
@ -232,6 +261,8 @@ namespace MobiusEditor.Tools
private void CommitChange()
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
var undoCellTriggers2 = new Dictionary<int, CellTrigger>(undoCellTriggers);
void undoAction(UndoRedoEventArgs e)
{
@ -243,6 +274,10 @@ namespace MobiusEditor.Tools
e.Map.CellTriggers[kv.Key] = isValid ? cellTrig : null;
}
e.MapPanel.Invalidate();
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
var redoCellTriggers2 = new Dictionary<int, CellTrigger>(redoCellTriggers);
void redoAction(UndoRedoEventArgs e)
@ -255,6 +290,10 @@ namespace MobiusEditor.Tools
e.Map.CellTriggers[kv.Key] = isValid ? cellTrig : null;
}
e.MapPanel.Invalidate();
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
undoCellTriggers.Clear();
redoCellTriggers.Clear();

View File

@ -163,7 +163,7 @@ namespace MobiusEditor.Tools
selectedObjectProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
AddUndoRedo(infantry, preEdit);
AddPropertiesUndoRedo(infantry, preEdit);
};
infantry.PropertyChanged += SelectedInfantry_PropertyChanged;
selectedObjectProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
@ -173,11 +173,17 @@ namespace MobiusEditor.Tools
}
}
private void AddUndoRedo(Infantry infantry, Infantry preEdit)
private void AddPropertiesUndoRedo(Infantry infantry, Infantry preEdit)
{
// infantry = infantry in its final edited form. Clone for preservation
Infantry redoInf = infantry.Clone();
Infantry undoInf = preEdit;
if (redoInf.Equals(undoInf))
{
return;
}
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
infantry.CloneDataFrom(undoInf);
@ -187,6 +193,10 @@ namespace MobiusEditor.Tools
infantry.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, infantry.InfantryGroup);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -197,6 +207,10 @@ namespace MobiusEditor.Tools
infantry.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, infantry.InfantryGroup);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -286,7 +300,6 @@ namespace MobiusEditor.Tools
}
selectedInfantry.InfantryGroup = infantryGroup;
mapPanel.Invalidate(map, infantryGroup);
plugin.Dirty = true;
}
if (infantryGroup == selectedInfantry.InfantryGroup)
{
@ -338,6 +351,8 @@ namespace MobiusEditor.Tools
int finalStop = Array.IndexOf(toMove.InfantryGroup.Infantry, selectedInfantry);
if (finalLocation.HasValue && finalStop != -1 && (finalLocation.Value != startLocation || finalStop != startStop))
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
Point endLocation = finalLocation.Value;
void undoAction(UndoRedoEventArgs ev)
{
@ -367,6 +382,10 @@ namespace MobiusEditor.Tools
ev.Map.Technos.Remove(finalGroup);
}
}
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -396,6 +415,10 @@ namespace MobiusEditor.Tools
ev.Map.Technos.Remove(startGroup);
}
}
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -446,6 +469,8 @@ namespace MobiusEditor.Tools
infantryGroup.Infantry[placeStop] = infantry;
infantry.InfantryGroup = infantryGroup;
mapPanel.Invalidate(map, infantryGroup);
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
InfantryGroup placeGroup = ev.Map.Technos[cell] as InfantryGroup;
@ -457,7 +482,10 @@ namespace MobiusEditor.Tools
{
ev.Map.Technos.Remove(placeGroup);
}
plugin.Dirty = true;
}
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
@ -475,9 +503,12 @@ namespace MobiusEditor.Tools
placeGroup.Infantry[placeStop] = infantry;
infantry.InfantryGroup = placeGroup;
}
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
break;
}
}
@ -502,11 +533,12 @@ namespace MobiusEditor.Tools
}
infantryGroup.Infantry[placeStop] = null;
mapPanel.Invalidate(map, infantryGroup);
plugin.Dirty = true;
if (infantryGroup.Infantry.All(x => x == null))
{
map.Technos.Remove(infantryGroup);
}
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
ICellOccupier occupier = ev.Map.Technos[cell];
@ -522,6 +554,10 @@ namespace MobiusEditor.Tools
placeGroup.Infantry[placeStop] = delInf;
delInf.InfantryGroup = placeGroup;
}
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -534,7 +570,10 @@ namespace MobiusEditor.Tools
{
ev.Map.Technos.Remove(placeGroup);
}
plugin.Dirty = true;
}
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);

View File

@ -180,7 +180,6 @@ namespace MobiusEditor.Tools
map.Overlay[cell] = overlay;
redoOverlays[cell] = overlay;
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1));
plugin.Dirty = true;
}
}
}
@ -197,18 +196,16 @@ namespace MobiusEditor.Tools
{
undoOverlays[cell] = map.Overlay[cell];
}
map.Overlay[cell] = null;
redoOverlays[cell] = null;
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1));
plugin.Dirty = true;
}
}
}
private void CommitChange()
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
var undoOverlays2 = new Dictionary<int, Overlay>(undoOverlays);
void undoAction(UndoRedoEventArgs e)
{
@ -221,8 +218,11 @@ namespace MobiusEditor.Tools
e.Map.Metrics.GetLocation(k, out Point location);
return Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1);
}));
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
var redoOverlays2 = new Dictionary<int, Overlay>(redoOverlays);
void redoAction(UndoRedoEventArgs e)
{
@ -235,11 +235,13 @@ namespace MobiusEditor.Tools
e.Map.Metrics.GetLocation(k, out Point location);
return Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1);
}));
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
undoOverlays.Clear();
redoOverlays.Clear();
url.Track(undoAction, redoAction);
}
@ -249,16 +251,12 @@ namespace MobiusEditor.Tools
{
return;
}
placementMode = true;
navigationWidget.MouseoverSize = Size.Empty;
if (SelectedOverlayType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}
@ -268,16 +266,12 @@ namespace MobiusEditor.Tools
{
return;
}
placementMode = false;
navigationWidget.MouseoverSize = new Size(1, 1);
if (SelectedOverlayType != null)
{
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(navigationWidget.MouseCell, new Size(1, 1)), 1, 1));
}
UpdateStatus();
}

View File

@ -188,7 +188,6 @@ namespace MobiusEditor.Tools
var overlay = new Overlay { Type = resourceType, Icon = 0 };
map.Overlay[cell] = overlay;
redoOverlays[cell] = overlay;
plugin.Dirty = true;
}
}
}
@ -214,7 +213,6 @@ namespace MobiusEditor.Tools
}
map.Overlay[cell] = null;
redoOverlays[cell] = null;
plugin.Dirty = true;
}
}
}
@ -246,6 +244,8 @@ namespace MobiusEditor.Tools
private void CommitChange()
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
var undoOverlays2 = new Dictionary<int, Overlay>(undoOverlays);
void undoAction(UndoRedoEventArgs e)
{
@ -260,6 +260,10 @@ namespace MobiusEditor.Tools
rectangle.Inflate(1, 1);
return rectangle;
}));
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
var redoOverlays2 = new Dictionary<int, Overlay>(redoOverlays);
@ -276,11 +280,13 @@ namespace MobiusEditor.Tools
rectangle.Inflate(1, 1);
return rectangle;
}));
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
undoOverlays.Clear();
redoOverlays.Clear();
url.Track(undoAction, redoAction);
}

View File

@ -123,7 +123,7 @@ namespace MobiusEditor.Tools
selectedSmudgeProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
AddUndoRedo(smudge, preEdit);
AddPropertiesUndoRedo(smudge, preEdit);
};
smudge.PropertyChanged += SelectedSmudge_PropertyChanged;
selectedSmudgeProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
@ -132,21 +132,34 @@ namespace MobiusEditor.Tools
}
}
private void AddUndoRedo(Smudge smudge, Smudge preEdit)
private void AddPropertiesUndoRedo(Smudge smudge, Smudge preEdit)
{
// building = building in its final edited form. Clone for preservation
// smudge = smudge in its final edited form. Clone for preservation
Smudge redoSmudge = smudge.Clone();
Smudge undoSmudge = preEdit;
preEdit = null;
if (redoSmudge.Equals(undoSmudge))
{
return;
}
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
smudge.CloneDataFrom(undoSmudge);
ev.MapPanel.Invalidate(ev.Map, smudge);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
smudge.CloneDataFrom(redoSmudge);
ev.MapPanel.Invalidate(ev.Map, smudge);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -286,6 +299,8 @@ namespace MobiusEditor.Tools
}
mapPanel.Invalidate(map, undomap.Keys);
mapPanel.Invalidate(map, redomap.Keys);
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, undomap.Keys);
@ -293,6 +308,10 @@ namespace MobiusEditor.Tools
{
e.Map.Smudge[p] = undomap[p];
}
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
@ -300,10 +319,12 @@ namespace MobiusEditor.Tools
{
e.Map.Smudge[p] = redomap[p];
}
e.MapPanel.Invalidate(e.Map, redomap.Keys);
e.MapPanel.Invalidate(e.Map, redomap.Keys); if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
private void RemoveSmudge(Point location)
@ -318,6 +339,8 @@ namespace MobiusEditor.Tools
RestoreNearbySmudge(map, undomap.Keys, redomap);
mapPanel.Invalidate(map, undomap.Keys);
mapPanel.Invalidate(map, redomap.Keys);
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
// TODO
void undoAction(UndoRedoEventArgs e)
{
@ -326,6 +349,10 @@ namespace MobiusEditor.Tools
e.Map.Smudge[p] = undomap[p];
}
e.MapPanel.Invalidate(e.Map, undomap.Keys);
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
@ -334,9 +361,12 @@ namespace MobiusEditor.Tools
{
e.Map.Smudge[p] = redomap[p];
}
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
private void RemoveSmudgePoints(Rectangle toClear, Dictionary<Point, Smudge> undomap, Dictionary<Point, Smudge> redomap)

View File

@ -413,7 +413,6 @@ namespace MobiusEditor.Tools
Point startPoint = navigationWidget.MouseCell;
dragStartPoint = map.Bounds.Contains(startPoint) ? (Point?)startPoint : null;
dragStartBounds = dragBounds;
//navigationWidget.CurrentCursor = Cursors.SizeAll;
UpdateStatus();
}
}
@ -641,6 +640,37 @@ namespace MobiusEditor.Tools
CommitTileChanges(true);
}
public static String RandomizeTiles(IGamePlugin plugin, MapPanel mapPanel, UndoRedoList<UndoRedoEventArgs> url)
{
Random rnd = new Random();
Dictionary<int, Template> undoTemplates = new Dictionary<int, Template>();
Dictionary<int, Template> redoTemplates = new Dictionary<int, Template>();
TemplateTypeFlag toRandomise = TemplateTypeFlag.RandomCell | TemplateTypeFlag.IsGrouped;
Map map = plugin.Map;
if (map.TemplateTypes.Where(t => t.Theaters == null || t.Theaters.Contains(map.Theater)).All(tm => (tm.Flag & toRandomise) == TemplateTypeFlag.None))
{
return "This map's theater does not contain randomizable tiles.";
}
int mapLength = map.Metrics.Length;
int mapWidth = map.Metrics.Width;
for (int i = 0; i < mapLength; ++i)
{
Point location = new Point(i / mapWidth, i % mapWidth);
TemplateType cur = PickTemplate(map, location, true, out _);
if (cur == null || (cur.Flag & TemplateTypeFlag.RandomCell) == TemplateTypeFlag.None)
{
continue;
}
SetTemplate(map, cur, location, null, undoTemplates, redoTemplates, rnd);
}
mapPanel.Invalidate(map, redoTemplates.Keys);
int count = undoTemplates.Count;
// This clears the undo/redo lists, so refresh and count needs to be taken first.
CommitTileChanges(url, undoTemplates, redoTemplates, plugin);
return String.Format("{0} cells replaced.", count);
}
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
{
CommitEdgeDrag();
@ -670,6 +700,15 @@ namespace MobiusEditor.Tools
{
ExitAllModes();
}
CheckBoundsCursor();
if (!boundsMode)
{
mouseTooltip.Hide(mapPanel);
}
}
private void CheckBoundsCursor()
{
var cursor = Cursors.Default;
if (boundsMode)
{
@ -692,7 +731,7 @@ namespace MobiusEditor.Tools
cursor = Cursors.SizeNWSE;
break;
case FacingType.None:
if (map.Bounds.Contains(navigationWidget.MouseCell) || dragStartPoint.HasValue && dragStartBounds.HasValue)
if (map.Bounds.Contains(navigationWidget.MouseCell) || (dragStartPoint.HasValue && dragStartBounds.HasValue))
{
cursor = Cursors.SizeAll;
}
@ -705,10 +744,6 @@ namespace MobiusEditor.Tools
UpdateTooltip();
}
}
if (!boundsMode)
{
mouseTooltip.Hide(mapPanel);
}
}
private void MouseoverWidget_ClosestMouseCellBorderChanged(object sender, MouseCellChangedEventArgs e)
@ -861,7 +896,18 @@ namespace MobiusEditor.Tools
private void SetTemplate(Point location, bool skipInvalidate)
{
TemplateType selected = SelectedTemplateType;
Dictionary<int, Template> addedRedoTemplates = new Dictionary<int, Template>();
SetTemplate(map, SelectedTemplateType, location, SelectedIcon, undoTemplates, addedRedoTemplates, random);
addedRedoTemplates.ToList().ForEach(kv => redoTemplates[kv.Key] = kv.Value);
if (!skipInvalidate)
{
mapPanel.Invalidate(map, addedRedoTemplates.Keys);
}
}
public static void SetTemplate(Map map, TemplateType selected, Point location, Point? SelectedIcon,
Dictionary<int, Template> undoTemplates, Dictionary<int, Template> redoTemplates, Random randomiser)
{
if (selected == null)
{
return;
@ -882,14 +928,9 @@ namespace MobiusEditor.Tools
placeType = map.TemplateTypes.Where(t => t.Name == selected.GroupTiles[icon]).FirstOrDefault();
icon = 0;
}
var template = new Template { Type = placeType, Icon = icon };
map.Templates[cell] = template;
redoTemplates[cell] = template;
if (!skipInvalidate)
{
mapPanel.Invalidate(map, cell);
}
plugin.Dirty = true;
var placeTemplate = new Template { Type = placeType, Icon = icon };
map.Templates[cell] = placeTemplate;
redoTemplates[cell] = placeTemplate;
}
}
else
@ -928,13 +969,13 @@ namespace MobiusEditor.Tools
TemplateType placeType = selected;
if (isGroup)
{
int randomType = random.Next(0, selected.NumIcons);
int randomType = randomiser.Next(0, selected.NumIcons);
placeType = map.TemplateTypes.Where(t => t.Name == selected.GroupTiles[randomType]).FirstOrDefault();
placeIcon = 0;
}
else if (isRandom)
{
placeIcon = random.Next(0, selected.NumIcons);
placeIcon = randomiser.Next(0, selected.NumIcons);
}
else
{
@ -943,11 +984,6 @@ namespace MobiusEditor.Tools
var template = new Template { Type = placeType, Icon = placeIcon };
map.Templates[cell] = template;
redoTemplates[cell] = template;
if (!skipInvalidate)
{
mapPanel.Invalidate(map, cell);
}
plugin.Dirty = true;
}
}
}
@ -968,7 +1004,6 @@ namespace MobiusEditor.Tools
map.Templates[cell] = null;
redoTemplates[cell] = null;
mapPanel.Invalidate(map, cell);
plugin.Dirty = true;
}
}
else
@ -1005,7 +1040,6 @@ namespace MobiusEditor.Tools
map.Templates[cell] = null;
redoTemplates[cell] = null;
mapPanel.Invalidate(map, cell);
plugin.Dirty = true;
}
}
}
@ -1069,6 +1103,7 @@ namespace MobiusEditor.Tools
mapPanel.Invalidate();
UpdateTooltip();
UpdateStatus();
CheckBoundsCursor();
}
private void ExitAllModes()
@ -1208,6 +1243,14 @@ namespace MobiusEditor.Tools
private void PickTemplate(Point location, bool wholeTemplate)
{
SelectedTemplateType = PickTemplate(map, location, wholeTemplate, out Point? selectedIcon);
SelectedIcon = selectedIcon;
}
public static TemplateType PickTemplate(Map map, Point location, bool wholeTemplate, out Point? selectedIcon)
{
TemplateType picked = null;
selectedIcon = null;
if (map.Metrics.GetCell(location, out int cell))
{
var template = map.Templates[cell];
@ -1218,18 +1261,18 @@ namespace MobiusEditor.Tools
{
groupOwned = true;
string owningType = template.Type.GroupTiles[0];
SelectedTemplateType = map.TemplateTypes.Where(t => t.Name.Equals(owningType, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
picked = map.TemplateTypes.Where(t => t.Name.Equals(owningType, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
}
else
{
SelectedTemplateType = template.Type;
picked = template.Type;
}
}
else
{
SelectedTemplateType = map.TemplateTypes.Where(t => t.Name.Equals("clear1", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
picked = map.TemplateTypes.Where(t => t.Name.Equals("clear1", StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
}
TemplateType selected = SelectedTemplateType;
TemplateType selected = picked;
bool isRandom = (selected.Flag & TemplateTypeFlag.RandomCell) != TemplateTypeFlag.None && selected.NumIcons > 1;
if (!wholeTemplate && ((selected.IconWidth * selected.IconHeight) > 1 || isRandom))
{
@ -1244,13 +1287,14 @@ namespace MobiusEditor.Tools
icon = template?.Icon ?? 0;
}
int width = selected.ThumbnailWidth;
SelectedIcon = new Point(icon % width, icon / width);
selectedIcon = new Point(icon % width, icon / width);
}
else
{
SelectedIcon = null;
selectedIcon = null;
}
}
return picked;
}
private FacingType DetectDragEdge()
@ -1331,18 +1375,27 @@ namespace MobiusEditor.Tools
{
var oldBounds = map.Bounds;
var newBounds = dragBounds;
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ure)
{
ure.Map.Bounds = oldBounds;
ure.MapPanel.Invalidate();
if (ure.Plugin != null)
{
ure.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ure)
{
ure.Map.Bounds = newBounds;
ure.MapPanel.Invalidate();
if (ure.Plugin != null)
{
ure.Plugin.Dirty = true;
}
}
map.Bounds = newBounds;
plugin.Dirty = true;
url.Track(undoAction, redoAction);
}
dragEdge = FacingType.None;
@ -1354,11 +1407,22 @@ namespace MobiusEditor.Tools
private void CommitTileChanges(bool noCheck)
{
if ((!noCheck && !placementMode) || (undoTemplates.Count == 0) || (redoTemplates.Count == 0))
if (!noCheck && !placementMode)
{
return;
}
CommitTileChanges(this.url, this.undoTemplates, this.redoTemplates, plugin);
}
private static void CommitTileChanges(UndoRedoList<UndoRedoEventArgs> url, Dictionary<int, Template> undoTemplates, Dictionary<int, Template> redoTemplates, IGamePlugin plugin)
{
if (undoTemplates.Count == 0 || redoTemplates.Count == 0)
{
return;
}
var undoTemplates2 = new Dictionary<int, Template>(undoTemplates);
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs e)
{
foreach (var kv in undoTemplates2)
@ -1366,6 +1430,10 @@ namespace MobiusEditor.Tools
e.Map.Templates[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate(e.Map, undoTemplates2.Keys);
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
var redoTemplates2 = new Dictionary<int, Template>(redoTemplates);
void redoAction(UndoRedoEventArgs e)
@ -1375,6 +1443,10 @@ namespace MobiusEditor.Tools
e.Map.Templates[kv.Key] = kv.Value;
}
e.MapPanel.Invalidate(e.Map, redoTemplates2.Keys);
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
undoTemplates.Clear();
redoTemplates.Clear();

View File

@ -71,7 +71,6 @@ namespace MobiusEditor.Tools
mapPanel.Invalidate(map, new Rectangle(navigationWidget.MouseCell, selectedTerrainType.OverlapBounds.Size));
}
mockTerrain.Type = selectedTerrainType;
mockTerrain.Icon = selectedTerrainType.DisplayIcon;
RefreshPreviewPanel();
}
}
@ -121,7 +120,7 @@ namespace MobiusEditor.Tools
selectedTerrainProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
AddUndoRedo(terrain, preEdit);
AddPropertiesUndoRedo(terrain, preEdit);
};
selectedTerrainProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
}
@ -130,30 +129,44 @@ namespace MobiusEditor.Tools
}
}
private void AddUndoRedo(Terrain terrain, Terrain preEdit)
private void AddPropertiesUndoRedo(Terrain terrain, Terrain preEdit)
{
// terrain = terrain in its final edited form. Clone for preservation
Terrain redoUnit = terrain.Clone();
Terrain undoUnit = preEdit;
Terrain redoTerr = terrain.Clone();
Terrain undoTerr = preEdit;
if (redoTerr.Equals(undoTerr))
{
return;
}
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
terrain.CloneDataFrom(undoUnit);
terrain.CloneDataFrom(undoTerr);
if (terrain.Trigger == null || (!Trigger.None.Equals(terrain.Trigger, StringComparison.InvariantCultureIgnoreCase)
&& !ev.Map.FilterTerrainTriggers().Any(tr => tr.Name.Equals(terrain.Trigger, StringComparison.InvariantCultureIgnoreCase))))
{
terrain.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, terrain);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
terrain.CloneDataFrom(redoUnit);
terrain.CloneDataFrom(redoTerr);
if (terrain.Trigger == null || (!Trigger.None.Equals(terrain.Trigger, StringComparison.InvariantCultureIgnoreCase)
&& !ev.Map.FilterTerrainTriggers().Any(tr => tr.Name.Equals(terrain.Trigger, StringComparison.InvariantCultureIgnoreCase))))
{
terrain.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, terrain);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -225,12 +238,18 @@ namespace MobiusEditor.Tools
if (finalLocation.HasValue && finalLocation.Value != startLocation)
{
Point endLocation = finalLocation.Value;
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
ev.MapPanel.Invalidate(ev.Map, toMove);
ev.Map.Technos.Remove(toMove);
ev.Map.Technos.Add(startLocation, toMove);
ev.MapPanel.Invalidate(ev.Map, toMove);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -238,6 +257,10 @@ namespace MobiusEditor.Tools
ev.Map.Technos.Remove(toMove);
ev.Map.Technos.Add(endLocation, toMove);
ev.MapPanel.Invalidate(ev.Map, toMove);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -275,7 +298,6 @@ namespace MobiusEditor.Tools
if (map.Technos.Add(newLocation, toMove))
{
mapPanel.Invalidate(map, toMove);
plugin.Dirty = true;
}
else
{
@ -295,19 +317,28 @@ namespace MobiusEditor.Tools
var terrain = mockTerrain.Clone();
if (map.Technos.Add(location, terrain))
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
mapPanel.Invalidate(map, terrain);
void undoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, location);
e.Map.Technos.Remove(terrain);
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
e.Map.Technos.Add(location, terrain);
e.MapPanel.Invalidate(e.Map, location);
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
}
@ -318,18 +349,27 @@ namespace MobiusEditor.Tools
{
mapPanel.Invalidate(map, terrain);
map.Technos.Remove(location);
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs e)
{
e.Map.Technos.Add(location, terrain);
e.MapPanel.Invalidate(e.Map, terrain);
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, terrain);
e.Map.Technos.Remove(terrain);
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
@ -461,7 +501,6 @@ namespace MobiusEditor.Tools
var terrain = new Terrain
{
Type = SelectedTerrainType,
Icon = SelectedTerrainType.DisplayIcon,
Tint = Color.FromArgb(128, Color.White)
};
previewMap.Technos.Add(location, terrain);

View File

@ -156,7 +156,7 @@ namespace MobiusEditor.Tools
selectedObjectProperties.Closed += (cs, ce) =>
{
navigationWidget.Refresh();
AddUndoRedo(unit, preEdit);
AddPropertiesUndoRedo(unit, preEdit);
};
unit.PropertyChanged += SelectedUnit_PropertyChanged;
selectedObjectProperties.Show(mapPanel, mapPanel.PointToClient(Control.MousePosition));
@ -165,11 +165,17 @@ namespace MobiusEditor.Tools
}
}
private void AddUndoRedo(Unit unit, Unit preEdit)
private void AddPropertiesUndoRedo(Unit unit, Unit preEdit)
{
// unit = unit in its final edited form. Clone for preservation
Unit redoUnit = unit.Clone();
Unit undoUnit = preEdit;
if (redoUnit.Equals(undoUnit))
{
return;
}
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
unit.CloneDataFrom(undoUnit);
@ -179,6 +185,10 @@ namespace MobiusEditor.Tools
unit.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, unit);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -189,6 +199,10 @@ namespace MobiusEditor.Tools
unit.Trigger = Trigger.None;
}
ev.MapPanel.Invalidate(ev.Map, unit);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -282,12 +296,18 @@ namespace MobiusEditor.Tools
if (finalLocation.HasValue && finalLocation.Value != startLocation)
{
Point endLocation = finalLocation.Value;
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs ev)
{
ev.MapPanel.Invalidate(ev.Map, toMove);
ev.Map.Technos.Remove(toMove);
ev.Map.Technos.Add(startLocation, toMove);
ev.MapPanel.Invalidate(ev.Map, toMove);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs ev)
{
@ -295,6 +315,10 @@ namespace MobiusEditor.Tools
ev.Map.Technos.Remove(toMove);
ev.Map.Technos.Add(endLocation, toMove);
ev.MapPanel.Invalidate(ev.Map, toMove);
if (ev.Plugin != null)
{
ev.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
}
@ -318,7 +342,6 @@ namespace MobiusEditor.Tools
if (map.Technos.Add(e.NewCell, selectedUnit))
{
mapPanel.Invalidate(map, selectedUnit);
plugin.Dirty = true;
}
else
{
@ -334,19 +357,28 @@ namespace MobiusEditor.Tools
var unit = mockUnit.Clone();
if (map.Technos.Add(location, unit))
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
mapPanel.Invalidate(map, unit);
void undoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, unit);
e.Map.Technos.Remove(unit);
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
e.Map.Technos.Add(location, unit);
e.MapPanel.Invalidate(e.Map, unit);
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}
}
@ -357,18 +389,27 @@ namespace MobiusEditor.Tools
{
mapPanel.Invalidate(map, unit);
map.Technos.Remove(unit);
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
void undoAction(UndoRedoEventArgs e)
{
e.Map.Technos.Add(location, unit);
e.MapPanel.Invalidate(e.Map, unit);
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
void redoAction(UndoRedoEventArgs e)
{
e.MapPanel.Invalidate(e.Map, unit);
e.Map.Technos.Remove(unit);
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
url.Track(undoAction, redoAction);
plugin.Dirty = true;
}
}

View File

@ -201,6 +201,12 @@ namespace MobiusEditor.Tools
RefreshPreviewPanel();
}
private void Map_MapContentsChanged(Object sender, MapRefreshEventArgs e)
{
// General event meant to refresh map.
mapPanel.Invalidate(map, e.Points);
}
private void MapPanel_PreRender(object sender, RenderEventArgs e)
{
if ((e.Points != null) && (e.Points.Count == 0))
@ -273,10 +279,11 @@ namespace MobiusEditor.Tools
/// </summary>
public virtual void Activate()
{
navigationWidget.Activate();
this.navigationWidget.Activate();
this.mapPanel.PreRender += MapPanel_PreRender;
this.mapPanel.PostRender += MapPanel_PostRender;
this.map.RulesChanged += this.Map_RulesChanged;
this.map.MapContentsChanged += this.Map_MapContentsChanged;
}
/// <summary>
@ -287,10 +294,11 @@ namespace MobiusEditor.Tools
/// </summary>
public virtual void Deactivate()
{
navigationWidget.Deactivate();
mapPanel.PreRender -= MapPanel_PreRender;
mapPanel.PostRender -= MapPanel_PostRender;
this.navigationWidget.Deactivate();
this.mapPanel.PreRender -= MapPanel_PreRender;
this.mapPanel.PostRender -= MapPanel_PostRender;
this.map.RulesChanged -= this.Map_RulesChanged;
this.map.MapContentsChanged -= this.Map_MapContentsChanged;
}
#region IDisposable Support

View File

@ -179,7 +179,6 @@ namespace MobiusEditor.Tools
map.Overlay[cell] = overlay;
redoOverlays[cell] = overlay;
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1));
plugin.Dirty = true;
}
}
}
@ -199,13 +198,14 @@ namespace MobiusEditor.Tools
map.Overlay[cell] = null;
redoOverlays[cell] = null;
mapPanel.Invalidate(map, Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1));
plugin.Dirty = true;
}
}
}
private void CommitChange()
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
var undoOverlays2 = new Dictionary<int, Overlay>(undoOverlays);
void undoAction(UndoRedoEventArgs e)
{
@ -218,6 +218,10 @@ namespace MobiusEditor.Tools
e.Map.Metrics.GetLocation(k, out Point location);
return Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1);
}));
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
var redoOverlays2 = new Dictionary<int, Overlay>(redoOverlays);
void redoAction(UndoRedoEventArgs e)
@ -231,6 +235,10 @@ namespace MobiusEditor.Tools
e.Map.Metrics.GetLocation(k, out Point location);
return Rectangle.Inflate(new Rectangle(location, new Size(1, 1)), 1, 1);
}));
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
undoOverlays.Clear();
redoOverlays.Clear();

View File

@ -20,7 +20,6 @@ using MobiusEditor.Render;
using MobiusEditor.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
@ -83,6 +82,10 @@ namespace MobiusEditor.Tools
{
EnterPlacementMode();
}
else
{
CheckSelectShortcuts(e);
}
}
private void WaypointsTool_KeyUp(object sender, KeyEventArgs e)
@ -135,7 +138,6 @@ namespace MobiusEditor.Tools
waypointCombo.Items.Clear();
waypointCombo.DataSource = wp.ToArray();
waypointCombo.SelectedIndex = selected;
plugin.Dirty = true;
}
}
}
@ -174,12 +176,63 @@ namespace MobiusEditor.Tools
waypointCombo.Items.Clear();
waypointCombo.DataSource = wp.ToArray();
waypointCombo.SelectedIndex = waypointIndex;
plugin.Dirty = true;
}
}
}
private void CheckSelectShortcuts(KeyEventArgs e)
{
int maxVal = waypointCombo.Items.Count - 1;
int curVal = waypointCombo.SelectedIndex;
int? newVal = null;
if (Control.ModifierKeys == Keys.Shift)
{
switch (e.KeyCode)
{
case (Keys.F):
newVal = Enumerable.Range(0, map.Waypoints.Length).Cast<int?>().Where(i => (map.Waypoints[i.Value].Flag & WaypointFlag.Flare) == WaypointFlag.Flare).FirstOrDefault();
break;
case (Keys.H):
newVal = Enumerable.Range(0, map.Waypoints.Length).Cast<int?>().Where(i => (map.Waypoints[i.Value].Flag & WaypointFlag.Home) == WaypointFlag.Home).FirstOrDefault();
break;
case (Keys.R):
newVal = Enumerable.Range(0, map.Waypoints.Length).Cast<int?>().Where(i => (map.Waypoints[i.Value].Flag & WaypointFlag.Reinforce) == WaypointFlag.Reinforce).FirstOrDefault();
break;
case (Keys.S):
newVal = Enumerable.Range(0, map.Waypoints.Length).Cast<int?>().Where(i => (map.Waypoints[i.Value].Flag & WaypointFlag.Special) == WaypointFlag.Special).FirstOrDefault();
break;
}
}
else
{
switch (e.KeyCode)
{
case Keys.Home:
newVal = 0;
break;
case Keys.End:
newVal = maxVal;
break;
case Keys.PageDown:
newVal = Math.Min(curVal + 10, maxVal);
break;
case Keys.PageUp:
newVal = Math.Max(curVal - 10, 0);
break;
case Keys.Down:
newVal = Math.Min(curVal + 1, maxVal);
break;
case Keys.Up:
newVal = Math.Max(curVal - 1, 0);
break;
}
}
if (newVal.HasValue && curVal != newVal.Value)
{
waypointCombo.SelectedIndex = newVal.Value;
}
}
private void EnterPlacementMode()
{
if (placementMode)
@ -238,17 +291,27 @@ namespace MobiusEditor.Tools
private void CommitChange()
{
bool origDirtyState = plugin.Dirty;
plugin.Dirty = true;
var undoWaypoint2 = undoWaypoint;
void undoAction(UndoRedoEventArgs e)
{
undoWaypoint2.Value.waypoint.Cell = undoWaypoint2.Value.cell;
mapPanel.Invalidate();
if (e.Plugin != null)
{
e.Plugin.Dirty = origDirtyState;
}
}
var redoWaypoint2 = redoWaypoint;
void redoAction(UndoRedoEventArgs e)
{
redoWaypoint2.Value.waypoint.Cell = redoWaypoint2.Value.cell;
mapPanel.Invalidate();
if (e.Plugin != null)
{
e.Plugin.Dirty = true;
}
}
undoWaypoint = null;
redoWaypoint = null;
@ -302,13 +365,37 @@ namespace MobiusEditor.Tools
private void UpdateStatus()
{
WaypointFlag flag = WaypointFlag.None;
Waypoint[] wps = map.Waypoints;
int wplen = map.Waypoints.Length;
for (int i = 0; i < wplen; ++i) {
flag |= wps[i].Flag;
}
List<String> specialKeys = new List<string>();
if ((flag & WaypointFlag.Flare) != WaypointFlag.None)
{
specialKeys.Add("F");
}
if ((flag & WaypointFlag.Home) != WaypointFlag.None)
{
specialKeys.Add("H");
}
if ((flag & WaypointFlag.Reinforce) != WaypointFlag.None)
{
specialKeys.Add("R");
}
if ((flag & WaypointFlag.Special) != WaypointFlag.None)
{
specialKeys.Add("S");
}
if (placementMode)
{
statusLbl.Text = "Left-Click to set cell waypoint, Right-Click to clear cell waypoint";
statusLbl.Text = "Left-Click to set cell waypoint, Right-Click to clear cell waypoint, " + String.Join("/", specialKeys) + " to select a special waypoint";
}
else
{
statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick cell waypoint";
statusLbl.Text = "Shift to enter placement mode, Left-Click or Right-Click to pick cell waypoint, Shift + " + String.Join("/", specialKeys) + " to select a special waypoint";
}
}

View File

@ -0,0 +1,220 @@
using System;
using System.Windows.Forms;
namespace MobiusEditor.Utility
{
/// <summary>
/// List of scan codes for standard 104-key keyboard US English keyboard,
/// from https://stackoverflow.com/questions/2569268/net-difference-between-right-shift-and-left-shift-keys
/// and https://handmade.network/forums/articles/t/2823-keyboard_inputs_-_scancodes%252C_raw_input%252C_text_input%252C_key_names
/// </summary>
public enum OemScanCode
{
Escape = 1,
/// <summary>1 !</summary>
Number1 = 2,
/// <summary>2 @</summary>
Number2 = 3,
/// <summary>3 #</summary>
Number3 = 4,
/// <summary>4 $</summary>
Number4 = 5,
/// <summary>5 %</summary>
Number5 = 6,
/// <summary>6 ^</summary>
Number6 = 7,
/// <summary>7 &</summary>
Number7 = 8,
/// <summary>8 *</summary>
Number8 = 9,
/// <summary>9 (</summary>
Number9 = 0x0A,
/// <summary>0 )</summary>
Number0 = 0x0B,
/// <summary>- _</summary>
MinusDash = 0x0C,
/// <summary>= +</summary>
Equals = 0x0D,
Backspace = 0x0E,
Tab = 0x0F,
Q = 0x10,
W = 0x11,
E = 0x12,
R = 0x13,
T = 0x14,
Y = 0x15,
U = 0x16,
I = 0x17,
O = 0x18,
P = 0x19,
/// <summary>[ {</summary>
LeftBracket = 0x1A,
/// <summary>] }</summary>
RightBracket = 0x1B,
Enter = 0x1C,
LeftControl = 0x1D,
A = 0x1E,
S = 0x1F,
D = 0x20,
F = 0x21,
G = 0x22,
H = 0x23,
J = 0x24,
K = 0x25,
L = 0x26,
/// <summary>; :</summary>
SemiColon = 0x27,
/// <summary>' "</summary>
Quotes = 0x28,
/// <summary>` ~</summary>
BacktickTilde = 0x29,
LeftShift = 0x2A,
/// <summary>| \</summary>
Pipe = 0x2B,
Z = 0x2C,
X = 0x2D,
C = 0x2E,
V = 0x2F,
B = 0x30,
N = 0x31,
M = 0x32,
/// <summary>, &lt;</summary>
Comma = 0x33,
/// <summary>. &gt;</summary>
Period = 0x34,
/// <summary>/ ?</summary>
Slash = 0x35,
RightShift = 0x36,
NumPadAsterisk = 0x37,
LeftAlt = 0x38,
SpaceBar = 0x39,
CapsLock = 0x3A,
F1 = 0x3B,
F2 = 0x3C,
F3 = 0x3D,
F4 = 0x3E,
F5 = 0x3F,
F6 = 0x40,
F7 = 0x41,
F8 = 0x42,
F9 = 0x43,
F10 = 0x44,
PauseBreak = 0x45,
ScrollLock = 0x46,
NumPad7 = 0x47,
NumPad8 = 0x48,
NumPad9 = 0x49,
NumPadMinus = 0x4A,
NumPad4 = 0x4B,
NumPad5 = 0x4C,
NumPad6 = 0x4D,
NumPadPlus = 0x4E,
NumPad1 = 0x4F,
NumPad2 = 0x50,
NumPad3 = 0x51,
NumPad0 = 0x52,
NumPadDecimal = 0x53,
/// <summary> Alt + print screen. MapVirtualKeyEx( VK_SNAPSHOT, MAPVK_VK_TO_VSC_EX, 0 ) returns scancode 0x54.</summary>
AltPrintScreen = 0x54,
/// <summary>Key between the left shift and Z.</summary>
BracketAngle = 0x56,
F11 = 0x57,
F12 = 0x58,
OEM1 = 0x5a, /* VK_OEM_WSCTRL */
OEM2 = 0x5b, /* VK_OEM_FINISH */
OEM3 = 0x5c, /* VK_OEM_JUMP */
EraseEOF = 0x5d,
OEM4 = 0x5e, /* VK_OEM_BACKTAB */
OEM5 = 0x5f, /* VK_OEM_AUTO */
Zoom = 0x62,
Help = 0x63,
F13 = 0x64,
F14 = 0x65,
F15 = 0x66,
F16 = 0x67,
F17 = 0x68,
F18 = 0x69,
F19 = 0x6a,
F20 = 0x6b,
F21 = 0x6c,
F22 = 0x6d,
F23 = 0x6e,
OEM6 = 0x6f, /* VK_OEM_PA3 */
Katakana = 0x70,
OEM7 = 0x71, /* VK_OEM_RESET */
//---
F24 = 0x76,
SBCSChar = 0x77,
//---
Convert = 0x79,
//---
Nonconvert = 0x7B, /* VK_OEM_PA1 */
//---
RControl = 0x11D,
NumPadEnter = 0x11C,
//---
NumPadSlash = 0x135,
//---
PrintScreen = 0x137,
RightAlt = 0x138,
//---
NumLock = 0x145,
//---
Home = 0x147,
UpArrow = 0x148,
PageUp = 0x149,
//---
LeftArrow = 0x14B,
//---
RightArrow = 0x14D,
//---
End = 0x14F,
DownArrow = 0x150,
PageDown = 0x151,
Insert = 0x152,
Delete = 0x153,
//---
LeftWindows = 0x15B,
RightWindows = 0x15C,
/// <summary>The menu key thingy</summary>
Application = 0x15D,
}
public class Keyboard
{
/// <summary>Shift key</summary>
const int VK_SHIFT = 0x10;
/// <summary>Control key</summary>
const int VK_CONTROL = 0x11;
/// <summary>Alt key</summary>
const int VK_MENU = 0x12;
public static OemScanCode GetScanCodeFromLParam(int lParam)
{
return (OemScanCode)((lParam >> 16) & 0x1FF);
}
public static OemScanCode GetScanCode(Message msg)
{
return GetScanCodeFromLParam((int)msg.LParam);
}
public static bool HasShift(Message msg)
{
return (int)msg.WParam == VK_SHIFT;
}
public static bool HasControl(Message msg)
{
return (int)msg.WParam == VK_CONTROL;
}
public static bool HasAlt(Message msg)
{
return (int)msg.WParam == VK_MENU;
}
}
}

View File

@ -103,6 +103,8 @@ namespace MobiusEditor.Utility
.ToDictionary(k => k.Name, v => v);
}
public bool HasChanges => propertyValues.Count > 0;
public void Revert() => propertyValues.Clear();
public void Commit()
@ -168,6 +170,25 @@ namespace MobiusEditor.Utility
return true;
}
public bool TrySetMember(string memberName, object value)
{
if (!trackableProperties.TryGetValue(memberName, out PropertyInfo property))
{
return false;
}
if (Equals(property.GetValue(Object), value))
{
propertyValues.Remove(memberName);
}
else
{
propertyValues[memberName] = value;
NotifyPropertyChanged(memberName);
}
return true;
}
public void NotifyPropertyChanged(String name)
{
if (this.PropertyChanged != null)

View File

@ -332,6 +332,19 @@ These options are all enabled by default, but can be disabled if you wish. Use t
* Fixed a crash in the smudge restore system when you delete a smudge or building that is too close to the map edge.
* Reduced maximum multiplayer start positions in the editor to 8, since the games apparently can't show more.
#### v1.4.3.3: [WIP]
* When your mouse cursor is inside the map bounds and you press [Ctrl] in Map mode to enable bounds editing mode, your cursor will now immediately change to the Move cursor, without requiring any mouse movement.
* The status bar at the bottom will now explicitly mention the sub-position of the infantry under the mouse cursor.
* When loading a map, if a map's file name identifies it as classic single player mission, this will no longer mark the mission as "modified" by the loading process. This will make it simpler to open classic maps for reference without getting a save prompt on close. Do note that lots of classic maps contain errors in triggers being linked to wrong objects, and the automatic fixes for that **will** still mark the map as modified.
* Fixed issues with the editor window getting focused simply by moving the mouse over it. The main window can steal focus from the tool window, but not the other way around.
* In Waypoints and Cell Triggers editing mode, the Up, Down, PageUp, PageDown, Home and End keys will now let you go through the dropdown items list. PageUp and PageDown will skip 10 items.
* In Waypoints editing mode, pressing [Shift] and a key will select special waypoints: [F]lare, [H]ome, [R]einforce, [S]pecial.
* The shortcut keys to select the different editing modes (normally Q, W, E, R, T, Y, A, S, D, F, G) now work on keyboard position, meaning thy will also work in the logical way on on AZERTY keyboards.
* Fixed bug where the checked states of the Houses in the Map Settings would reset when changing the "Single-Player" checkbox.
* The [Aftermath] section is no longer ignored in the map settings' Rules editor, so Aftermath detail settings can be added. The actual "NewUnitsEnabled" setting in this is ignored, though; toggling the expansion units can only be done in the Basic settings.
* Undo/Redo tracking will now also undo the map's modified-status.
### Possible future features
Some ideas that might get implemented in the future: