-Added reset function for the SS crate settings

-Haystacks are now usable in Winter
-Tilesets are now searched in the given order
-Mods are separated into a setting per game
-Trigger and teamtype names are now always restricted to respectively 4 and 8 characters.
-Adjusted player settings to SS
-Fixed triggers being selectable on unbuilt buildings.
-Error messages added during load no longer implicitly mark the map as modified; a specific "modified" boolean is now added for that.
-Pressing [Enter] in Waypoints mode will now jump to the selected waypoint.
This commit is contained in:
Nyeguds 2022-09-29 07:50:41 +02:00
parent 9c91112cd5
commit f1fac05636
26 changed files with 664 additions and 337 deletions

View File

@ -13,8 +13,14 @@
</startup>
<applicationSettings>
<MobiusEditor.Properties.Settings>
<setting name="ModsToLoad" serializeAs="String">
<value>2844969675;Tiberian_Dawn\ConcretePavementTD</value>
<setting name="ModsToLoadTD" serializeAs="String">
<value>2844969675;ConcretePavementTD</value>
</setting>
<setting name="ModsToLoadRA" serializeAs="String">
<value />
</setting>
<setting name="ModsToLoadSS" serializeAs="String">
<value>2844969675;ConcretePavementTD</value>
</setting>
<setting name="MapScale" serializeAs="String">
<value>0.5</value>

View File

@ -83,6 +83,8 @@ namespace MobiusEditor.Controls
this.nudDensity = new MobiusEditor.Controls.EnhNumericUpDown();
this.nudIonFactor = new MobiusEditor.Controls.EnhNumericUpDown();
this.nudCrateTimer = new MobiusEditor.Controls.EnhNumericUpDown();
this.panel1 = new System.Windows.Forms.Panel();
this.btnDefaults = new System.Windows.Forms.Button();
this.tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.nudAddStrength)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.nudAddWeapon)).BeginInit();
@ -103,15 +105,17 @@ namespace MobiusEditor.Controls
((System.ComponentModel.ISupportInitialize)(this.nudDensity)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.nudIonFactor)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.nudCrateTimer)).BeginInit();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tableLayoutPanel1.AutoSize = true;
this.tableLayoutPanel1.ColumnCount = 3;
this.tableLayoutPanel1.ColumnCount = 2;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 35F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F));
this.tableLayoutPanel1.Controls.Add(this.label5, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.label4, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2);
@ -152,7 +156,6 @@ namespace MobiusEditor.Controls
this.tableLayoutPanel1.Controls.Add(this.nudDensity, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.nudIonFactor, 1, 2);
this.tableLayoutPanel1.Controls.Add(this.nudCrateTimer, 1, 3);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 25;
@ -181,7 +184,7 @@ namespace MobiusEditor.Controls
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(400, 645);
this.tableLayoutPanel1.Size = new System.Drawing.Size(463, 554);
this.tableLayoutPanel1.TabIndex = 1;
//
// label5
@ -191,7 +194,7 @@ namespace MobiusEditor.Controls
this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label5.Location = new System.Drawing.Point(3, 104);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(334, 26);
this.label5.Size = new System.Drawing.Size(457, 26);
this.label5.TabIndex = 38;
this.label5.Text = "Crate chance settings:";
this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -202,7 +205,7 @@ namespace MobiusEditor.Controls
this.label4.Location = new System.Drawing.Point(2, 78);
this.label4.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(136, 26);
this.label4.Size = new System.Drawing.Size(186, 26);
this.label4.TabIndex = 37;
this.label4.Text = "Crate Timer";
this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -213,7 +216,7 @@ namespace MobiusEditor.Controls
this.label3.Location = new System.Drawing.Point(2, 52);
this.label3.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(136, 26);
this.label3.Size = new System.Drawing.Size(186, 26);
this.label3.TabIndex = 36;
this.label3.Text = "Ion Factor";
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -225,7 +228,7 @@ namespace MobiusEditor.Controls
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Underline, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.Location = new System.Drawing.Point(3, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(334, 26);
this.label2.Size = new System.Drawing.Size(457, 26);
this.label2.TabIndex = 34;
this.label2.Text = "General crate settings:";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -236,7 +239,7 @@ namespace MobiusEditor.Controls
this.label1.Location = new System.Drawing.Point(2, 26);
this.label1.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(136, 26);
this.label1.Size = new System.Drawing.Size(186, 26);
this.label1.TabIndex = 33;
this.label1.Text = "Crate Density";
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -247,7 +250,7 @@ namespace MobiusEditor.Controls
this.lblStealth.Location = new System.Drawing.Point(2, 313);
this.lblStealth.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblStealth.Name = "lblStealth";
this.lblStealth.Size = new System.Drawing.Size(136, 26);
this.lblStealth.Size = new System.Drawing.Size(186, 26);
this.lblStealth.TabIndex = 14;
this.lblStealth.Text = "Crate: Stealth";
this.lblStealth.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -258,7 +261,7 @@ namespace MobiusEditor.Controls
this.lblSuper.Location = new System.Drawing.Point(2, 521);
this.lblSuper.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblSuper.Name = "lblSuper";
this.lblSuper.Size = new System.Drawing.Size(136, 26);
this.lblSuper.Size = new System.Drawing.Size(186, 26);
this.lblSuper.TabIndex = 30;
this.lblSuper.Text = "Crate: Super";
this.lblSuper.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -269,7 +272,7 @@ namespace MobiusEditor.Controls
this.lblArmageddon.Location = new System.Drawing.Point(2, 495);
this.lblArmageddon.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblArmageddon.Name = "lblArmageddon";
this.lblArmageddon.Size = new System.Drawing.Size(136, 26);
this.lblArmageddon.Size = new System.Drawing.Size(186, 26);
this.lblArmageddon.TabIndex = 28;
this.lblArmageddon.Text = "Crate: Armageddon";
this.lblArmageddon.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -280,7 +283,7 @@ namespace MobiusEditor.Controls
this.lblRadar.Location = new System.Drawing.Point(2, 469);
this.lblRadar.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblRadar.Name = "lblRadar";
this.lblRadar.Size = new System.Drawing.Size(136, 26);
this.lblRadar.Size = new System.Drawing.Size(186, 26);
this.lblRadar.TabIndex = 26;
this.lblRadar.Text = "Crate: Radar";
this.lblRadar.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -291,7 +294,7 @@ namespace MobiusEditor.Controls
this.lblUnshroud.Location = new System.Drawing.Point(2, 443);
this.lblUnshroud.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblUnshroud.Name = "lblUnshroud";
this.lblUnshroud.Size = new System.Drawing.Size(136, 26);
this.lblUnshroud.Size = new System.Drawing.Size(186, 26);
this.lblUnshroud.TabIndex = 24;
this.lblUnshroud.Text = "Crate: Unshroud";
this.lblUnshroud.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -302,7 +305,7 @@ namespace MobiusEditor.Controls
this.lblReshroud.Location = new System.Drawing.Point(2, 417);
this.lblReshroud.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblReshroud.Name = "lblReshroud";
this.lblReshroud.Size = new System.Drawing.Size(136, 26);
this.lblReshroud.Size = new System.Drawing.Size(186, 26);
this.lblReshroud.TabIndex = 22;
this.lblReshroud.Text = "Crate: Reshroud";
this.lblReshroud.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -313,7 +316,7 @@ namespace MobiusEditor.Controls
this.lblUncloakAll.Location = new System.Drawing.Point(2, 391);
this.lblUncloakAll.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblUncloakAll.Name = "lblUncloakAll";
this.lblUncloakAll.Size = new System.Drawing.Size(136, 26);
this.lblUncloakAll.Size = new System.Drawing.Size(186, 26);
this.lblUncloakAll.TabIndex = 20;
this.lblUncloakAll.Text = "Crate: Uncloak All";
this.lblUncloakAll.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -324,7 +327,7 @@ namespace MobiusEditor.Controls
this.lblKill.Location = new System.Drawing.Point(2, 365);
this.lblKill.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblKill.Name = "lblKill";
this.lblKill.Size = new System.Drawing.Size(136, 26);
this.lblKill.Size = new System.Drawing.Size(186, 26);
this.lblKill.TabIndex = 18;
this.lblKill.Text = "Crate: Kill";
this.lblKill.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -335,7 +338,7 @@ namespace MobiusEditor.Controls
this.lblTeleport.Location = new System.Drawing.Point(2, 339);
this.lblTeleport.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblTeleport.Name = "lblTeleport";
this.lblTeleport.Size = new System.Drawing.Size(136, 26);
this.lblTeleport.Size = new System.Drawing.Size(186, 26);
this.lblTeleport.TabIndex = 16;
this.lblTeleport.Text = "Crate: Teleport";
this.lblTeleport.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -345,7 +348,7 @@ namespace MobiusEditor.Controls
this.lblAddStrength.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblAddStrength.Location = new System.Drawing.Point(3, 130);
this.lblAddStrength.Name = "lblAddStrength";
this.lblAddStrength.Size = new System.Drawing.Size(134, 26);
this.lblAddStrength.Size = new System.Drawing.Size(184, 26);
this.lblAddStrength.TabIndex = 0;
this.lblAddStrength.Text = "Crate: Add Strength";
this.lblAddStrength.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -355,7 +358,7 @@ namespace MobiusEditor.Controls
this.lblAddWeapon.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblAddWeapon.Location = new System.Drawing.Point(3, 156);
this.lblAddWeapon.Name = "lblAddWeapon";
this.lblAddWeapon.Size = new System.Drawing.Size(134, 26);
this.lblAddWeapon.Size = new System.Drawing.Size(184, 26);
this.lblAddWeapon.TabIndex = 2;
this.lblAddWeapon.Text = "Crate: Add Weapon";
this.lblAddWeapon.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -365,7 +368,7 @@ namespace MobiusEditor.Controls
this.lblAddSpeed.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblAddSpeed.Location = new System.Drawing.Point(3, 182);
this.lblAddSpeed.Name = "lblAddSpeed";
this.lblAddSpeed.Size = new System.Drawing.Size(134, 26);
this.lblAddSpeed.Size = new System.Drawing.Size(184, 26);
this.lblAddSpeed.TabIndex = 4;
this.lblAddSpeed.Text = "Crate: Add Speed";
this.lblAddSpeed.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -375,7 +378,7 @@ namespace MobiusEditor.Controls
this.lblRapidReload.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblRapidReload.Location = new System.Drawing.Point(3, 208);
this.lblRapidReload.Name = "lblRapidReload";
this.lblRapidReload.Size = new System.Drawing.Size(134, 26);
this.lblRapidReload.Size = new System.Drawing.Size(184, 26);
this.lblRapidReload.TabIndex = 6;
this.lblRapidReload.Text = "Crate: Rapid Reload";
this.lblRapidReload.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -385,7 +388,7 @@ namespace MobiusEditor.Controls
this.lblAddRange.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblAddRange.Location = new System.Drawing.Point(3, 234);
this.lblAddRange.Name = "lblAddRange";
this.lblAddRange.Size = new System.Drawing.Size(134, 27);
this.lblAddRange.Size = new System.Drawing.Size(184, 27);
this.lblAddRange.TabIndex = 8;
this.lblAddRange.Text = "Crate: Add Range";
this.lblAddRange.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -396,7 +399,7 @@ namespace MobiusEditor.Controls
this.lblHeal.Location = new System.Drawing.Point(2, 261);
this.lblHeal.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblHeal.Name = "lblHeal";
this.lblHeal.Size = new System.Drawing.Size(136, 26);
this.lblHeal.Size = new System.Drawing.Size(186, 26);
this.lblHeal.TabIndex = 10;
this.lblHeal.Text = "Crate: Heal";
this.lblHeal.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -407,7 +410,7 @@ namespace MobiusEditor.Controls
this.lblBomb.Location = new System.Drawing.Point(2, 287);
this.lblBomb.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0);
this.lblBomb.Name = "lblBomb";
this.lblBomb.Size = new System.Drawing.Size(136, 26);
this.lblBomb.Size = new System.Drawing.Size(186, 26);
this.lblBomb.TabIndex = 12;
this.lblBomb.Text = "Crate: Bomb";
this.lblBomb.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -421,7 +424,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudAddStrength.IntValue = 0;
this.nudAddStrength.Location = new System.Drawing.Point(143, 133);
this.nudAddStrength.Location = new System.Drawing.Point(193, 133);
this.nudAddStrength.Maximum = new decimal(new int[] {
2147483647,
0,
@ -431,7 +434,7 @@ namespace MobiusEditor.Controls
this.nudAddStrength.SelectedText = "";
this.nudAddStrength.SelectionLength = 0;
this.nudAddStrength.SelectionStart = 0;
this.nudAddStrength.Size = new System.Drawing.Size(194, 20);
this.nudAddStrength.Size = new System.Drawing.Size(267, 20);
this.nudAddStrength.TabIndex = 35;
//
// nudAddWeapon
@ -443,7 +446,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudAddWeapon.IntValue = 0;
this.nudAddWeapon.Location = new System.Drawing.Point(143, 159);
this.nudAddWeapon.Location = new System.Drawing.Point(193, 159);
this.nudAddWeapon.Maximum = new decimal(new int[] {
2147483647,
0,
@ -453,7 +456,7 @@ namespace MobiusEditor.Controls
this.nudAddWeapon.SelectedText = "";
this.nudAddWeapon.SelectionLength = 0;
this.nudAddWeapon.SelectionStart = 0;
this.nudAddWeapon.Size = new System.Drawing.Size(194, 20);
this.nudAddWeapon.Size = new System.Drawing.Size(267, 20);
this.nudAddWeapon.TabIndex = 35;
//
// nudAddSpeed
@ -465,7 +468,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudAddSpeed.IntValue = 0;
this.nudAddSpeed.Location = new System.Drawing.Point(143, 185);
this.nudAddSpeed.Location = new System.Drawing.Point(193, 185);
this.nudAddSpeed.Maximum = new decimal(new int[] {
2147483647,
0,
@ -475,7 +478,7 @@ namespace MobiusEditor.Controls
this.nudAddSpeed.SelectedText = "";
this.nudAddSpeed.SelectionLength = 0;
this.nudAddSpeed.SelectionStart = 0;
this.nudAddSpeed.Size = new System.Drawing.Size(194, 20);
this.nudAddSpeed.Size = new System.Drawing.Size(267, 20);
this.nudAddSpeed.TabIndex = 35;
//
// nudRapidReload
@ -487,7 +490,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudRapidReload.IntValue = 0;
this.nudRapidReload.Location = new System.Drawing.Point(143, 211);
this.nudRapidReload.Location = new System.Drawing.Point(193, 211);
this.nudRapidReload.Maximum = new decimal(new int[] {
2147483647,
0,
@ -497,7 +500,7 @@ namespace MobiusEditor.Controls
this.nudRapidReload.SelectedText = "";
this.nudRapidReload.SelectionLength = 0;
this.nudRapidReload.SelectionStart = 0;
this.nudRapidReload.Size = new System.Drawing.Size(194, 20);
this.nudRapidReload.Size = new System.Drawing.Size(267, 20);
this.nudRapidReload.TabIndex = 35;
//
// nudAddRange
@ -509,7 +512,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudAddRange.IntValue = 0;
this.nudAddRange.Location = new System.Drawing.Point(143, 237);
this.nudAddRange.Location = new System.Drawing.Point(193, 237);
this.nudAddRange.Maximum = new decimal(new int[] {
2147483647,
0,
@ -519,7 +522,7 @@ namespace MobiusEditor.Controls
this.nudAddRange.SelectedText = "";
this.nudAddRange.SelectionLength = 0;
this.nudAddRange.SelectionStart = 0;
this.nudAddRange.Size = new System.Drawing.Size(194, 20);
this.nudAddRange.Size = new System.Drawing.Size(267, 20);
this.nudAddRange.TabIndex = 35;
//
// nudHeal
@ -531,7 +534,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudHeal.IntValue = 0;
this.nudHeal.Location = new System.Drawing.Point(143, 264);
this.nudHeal.Location = new System.Drawing.Point(193, 264);
this.nudHeal.Maximum = new decimal(new int[] {
2147483647,
0,
@ -541,7 +544,7 @@ namespace MobiusEditor.Controls
this.nudHeal.SelectedText = "";
this.nudHeal.SelectionLength = 0;
this.nudHeal.SelectionStart = 0;
this.nudHeal.Size = new System.Drawing.Size(194, 20);
this.nudHeal.Size = new System.Drawing.Size(267, 20);
this.nudHeal.TabIndex = 35;
//
// nudBomb
@ -553,7 +556,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudBomb.IntValue = 0;
this.nudBomb.Location = new System.Drawing.Point(143, 290);
this.nudBomb.Location = new System.Drawing.Point(193, 290);
this.nudBomb.Maximum = new decimal(new int[] {
2147483647,
0,
@ -563,7 +566,7 @@ namespace MobiusEditor.Controls
this.nudBomb.SelectedText = "";
this.nudBomb.SelectionLength = 0;
this.nudBomb.SelectionStart = 0;
this.nudBomb.Size = new System.Drawing.Size(194, 20);
this.nudBomb.Size = new System.Drawing.Size(267, 20);
this.nudBomb.TabIndex = 35;
//
// nudStealth
@ -575,7 +578,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudStealth.IntValue = 0;
this.nudStealth.Location = new System.Drawing.Point(143, 316);
this.nudStealth.Location = new System.Drawing.Point(193, 316);
this.nudStealth.Maximum = new decimal(new int[] {
2147483647,
0,
@ -585,7 +588,7 @@ namespace MobiusEditor.Controls
this.nudStealth.SelectedText = "";
this.nudStealth.SelectionLength = 0;
this.nudStealth.SelectionStart = 0;
this.nudStealth.Size = new System.Drawing.Size(194, 20);
this.nudStealth.Size = new System.Drawing.Size(267, 20);
this.nudStealth.TabIndex = 35;
//
// nudTeleport
@ -597,7 +600,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudTeleport.IntValue = 0;
this.nudTeleport.Location = new System.Drawing.Point(143, 342);
this.nudTeleport.Location = new System.Drawing.Point(193, 342);
this.nudTeleport.Maximum = new decimal(new int[] {
2147483647,
0,
@ -607,7 +610,7 @@ namespace MobiusEditor.Controls
this.nudTeleport.SelectedText = "";
this.nudTeleport.SelectionLength = 0;
this.nudTeleport.SelectionStart = 0;
this.nudTeleport.Size = new System.Drawing.Size(194, 20);
this.nudTeleport.Size = new System.Drawing.Size(267, 20);
this.nudTeleport.TabIndex = 35;
//
// nudKill
@ -619,7 +622,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudKill.IntValue = 0;
this.nudKill.Location = new System.Drawing.Point(143, 368);
this.nudKill.Location = new System.Drawing.Point(193, 368);
this.nudKill.Maximum = new decimal(new int[] {
2147483647,
0,
@ -629,7 +632,7 @@ namespace MobiusEditor.Controls
this.nudKill.SelectedText = "";
this.nudKill.SelectionLength = 0;
this.nudKill.SelectionStart = 0;
this.nudKill.Size = new System.Drawing.Size(194, 20);
this.nudKill.Size = new System.Drawing.Size(267, 20);
this.nudKill.TabIndex = 35;
//
// nudUncloakAll
@ -641,7 +644,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudUncloakAll.IntValue = 0;
this.nudUncloakAll.Location = new System.Drawing.Point(143, 394);
this.nudUncloakAll.Location = new System.Drawing.Point(193, 394);
this.nudUncloakAll.Maximum = new decimal(new int[] {
2147483647,
0,
@ -651,7 +654,7 @@ namespace MobiusEditor.Controls
this.nudUncloakAll.SelectedText = "";
this.nudUncloakAll.SelectionLength = 0;
this.nudUncloakAll.SelectionStart = 0;
this.nudUncloakAll.Size = new System.Drawing.Size(194, 20);
this.nudUncloakAll.Size = new System.Drawing.Size(267, 20);
this.nudUncloakAll.TabIndex = 35;
//
// nudReshroud
@ -663,7 +666,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudReshroud.IntValue = 0;
this.nudReshroud.Location = new System.Drawing.Point(143, 420);
this.nudReshroud.Location = new System.Drawing.Point(193, 420);
this.nudReshroud.Maximum = new decimal(new int[] {
2147483647,
0,
@ -673,7 +676,7 @@ namespace MobiusEditor.Controls
this.nudReshroud.SelectedText = "";
this.nudReshroud.SelectionLength = 0;
this.nudReshroud.SelectionStart = 0;
this.nudReshroud.Size = new System.Drawing.Size(194, 20);
this.nudReshroud.Size = new System.Drawing.Size(267, 20);
this.nudReshroud.TabIndex = 35;
//
// nudUnshroud
@ -685,7 +688,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudUnshroud.IntValue = 0;
this.nudUnshroud.Location = new System.Drawing.Point(143, 446);
this.nudUnshroud.Location = new System.Drawing.Point(193, 446);
this.nudUnshroud.Maximum = new decimal(new int[] {
2147483647,
0,
@ -695,7 +698,7 @@ namespace MobiusEditor.Controls
this.nudUnshroud.SelectedText = "";
this.nudUnshroud.SelectionLength = 0;
this.nudUnshroud.SelectionStart = 0;
this.nudUnshroud.Size = new System.Drawing.Size(194, 20);
this.nudUnshroud.Size = new System.Drawing.Size(267, 20);
this.nudUnshroud.TabIndex = 35;
//
// nudRadar
@ -707,7 +710,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudRadar.IntValue = 0;
this.nudRadar.Location = new System.Drawing.Point(143, 472);
this.nudRadar.Location = new System.Drawing.Point(193, 472);
this.nudRadar.Maximum = new decimal(new int[] {
2147483647,
0,
@ -717,7 +720,7 @@ namespace MobiusEditor.Controls
this.nudRadar.SelectedText = "";
this.nudRadar.SelectionLength = 0;
this.nudRadar.SelectionStart = 0;
this.nudRadar.Size = new System.Drawing.Size(194, 20);
this.nudRadar.Size = new System.Drawing.Size(267, 20);
this.nudRadar.TabIndex = 35;
//
// nudArmageddon
@ -729,7 +732,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudArmageddon.IntValue = 0;
this.nudArmageddon.Location = new System.Drawing.Point(143, 498);
this.nudArmageddon.Location = new System.Drawing.Point(193, 498);
this.nudArmageddon.Maximum = new decimal(new int[] {
2147483647,
0,
@ -739,7 +742,7 @@ namespace MobiusEditor.Controls
this.nudArmageddon.SelectedText = "";
this.nudArmageddon.SelectionLength = 0;
this.nudArmageddon.SelectionStart = 0;
this.nudArmageddon.Size = new System.Drawing.Size(194, 20);
this.nudArmageddon.Size = new System.Drawing.Size(267, 20);
this.nudArmageddon.TabIndex = 35;
//
// nudSuper
@ -751,7 +754,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudSuper.IntValue = 0;
this.nudSuper.Location = new System.Drawing.Point(143, 524);
this.nudSuper.Location = new System.Drawing.Point(193, 524);
this.nudSuper.Maximum = new decimal(new int[] {
2147483647,
0,
@ -761,7 +764,7 @@ namespace MobiusEditor.Controls
this.nudSuper.SelectedText = "";
this.nudSuper.SelectionLength = 0;
this.nudSuper.SelectionStart = 0;
this.nudSuper.Size = new System.Drawing.Size(194, 20);
this.nudSuper.Size = new System.Drawing.Size(267, 20);
this.nudSuper.TabIndex = 35;
//
// nudDensity
@ -773,7 +776,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudDensity.IntValue = 0;
this.nudDensity.Location = new System.Drawing.Point(143, 29);
this.nudDensity.Location = new System.Drawing.Point(193, 29);
this.nudDensity.Maximum = new decimal(new int[] {
2147483647,
0,
@ -783,7 +786,7 @@ namespace MobiusEditor.Controls
this.nudDensity.SelectedText = "";
this.nudDensity.SelectionLength = 0;
this.nudDensity.SelectionStart = 0;
this.nudDensity.Size = new System.Drawing.Size(194, 20);
this.nudDensity.Size = new System.Drawing.Size(267, 20);
this.nudDensity.TabIndex = 35;
//
// nudIonFactor
@ -795,7 +798,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudIonFactor.IntValue = 0;
this.nudIonFactor.Location = new System.Drawing.Point(143, 55);
this.nudIonFactor.Location = new System.Drawing.Point(193, 55);
this.nudIonFactor.Maximum = new decimal(new int[] {
2147483647,
0,
@ -805,7 +808,7 @@ namespace MobiusEditor.Controls
this.nudIonFactor.SelectedText = "";
this.nudIonFactor.SelectionLength = 0;
this.nudIonFactor.SelectionStart = 0;
this.nudIonFactor.Size = new System.Drawing.Size(194, 20);
this.nudIonFactor.Size = new System.Drawing.Size(267, 20);
this.nudIonFactor.TabIndex = 35;
//
// nudCrateTimer
@ -817,7 +820,7 @@ namespace MobiusEditor.Controls
0,
0});
this.nudCrateTimer.IntValue = 0;
this.nudCrateTimer.Location = new System.Drawing.Point(143, 81);
this.nudCrateTimer.Location = new System.Drawing.Point(193, 81);
this.nudCrateTimer.Maximum = new decimal(new int[] {
2147483647,
0,
@ -827,18 +830,40 @@ namespace MobiusEditor.Controls
this.nudCrateTimer.SelectedText = "";
this.nudCrateTimer.SelectionLength = 0;
this.nudCrateTimer.SelectionStart = 0;
this.nudCrateTimer.Size = new System.Drawing.Size(194, 20);
this.nudCrateTimer.Size = new System.Drawing.Size(267, 20);
this.nudCrateTimer.TabIndex = 35;
//
// panel1
//
this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel1.AutoScroll = true;
this.panel1.Controls.Add(this.tableLayoutPanel1);
this.panel1.Location = new System.Drawing.Point(0, 4);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(485, 526);
this.panel1.TabIndex = 2;
//
// btnDefaults
//
this.btnDefaults.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.btnDefaults.Location = new System.Drawing.Point(365, 536);
this.btnDefaults.Name = "btnDefaults";
this.btnDefaults.Size = new System.Drawing.Size(117, 23);
this.btnDefaults.TabIndex = 3;
this.btnDefaults.Text = "Reset to defaults";
this.btnDefaults.UseVisualStyleBackColor = true;
this.btnDefaults.Click += new System.EventHandler(this.btnDefaults_Click);
//
// CrateSettings
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoScroll = true;
this.Controls.Add(this.tableLayoutPanel1);
this.Controls.Add(this.btnDefaults);
this.Controls.Add(this.panel1);
this.Name = "CrateSettings";
this.Size = new System.Drawing.Size(400, 645);
this.Load += new System.EventHandler(this.CrateSettings_Load);
this.Size = new System.Drawing.Size(485, 565);
this.tableLayoutPanel1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.nudAddStrength)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.nudAddWeapon)).EndInit();
@ -859,8 +884,9 @@ namespace MobiusEditor.Controls
((System.ComponentModel.ISupportInitialize)(this.nudDensity)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.nudIonFactor)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.nudCrateTimer)).EndInit();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
@ -907,5 +933,7 @@ namespace MobiusEditor.Controls
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Button btnDefaults;
}
}

View File

@ -7,45 +7,76 @@ namespace MobiusEditor.Controls
{
public partial class CrateSettings : UserControl
{
private PropertyTracker<SoleSurvivor.CratesSection> cratesSettings;
public CrateSettings(PropertyTracker<SoleSurvivor.CratesSection> cratesSection)
{
this.cratesSettings = cratesSection;
InitializeComponent();
nudAddStrength.DataBindings.Add("IntValue", cratesSection, "AddStrength");
nudAddWeapon.DataBindings.Add("IntValue", cratesSection, "AddWeapon");
nudAddSpeed.DataBindings.Add("IntValue", cratesSection, "AddSpeed");
nudRapidReload.DataBindings.Add("IntValue", cratesSection, "RapidReload");
nudAddRange.DataBindings.Add("IntValue", cratesSection, "AddRange");
nudHeal.DataBindings.Add("IntValue", cratesSection, "Heal");
nudBomb.DataBindings.Add("IntValue", cratesSection, "Bomb");
nudStealth.DataBindings.Add("IntValue", cratesSection, "Stealth");
nudTeleport.DataBindings.Add("IntValue", cratesSection, "Teleport");
nudKill.DataBindings.Add("IntValue", cratesSection, "Kill");
nudUncloakAll.DataBindings.Add("IntValue", cratesSection, "UncloakAll");
nudReshroud.DataBindings.Add("IntValue", cratesSection, "Reshroud");
nudUnshroud.DataBindings.Add("IntValue", cratesSection, "Unshroud");
nudRadar.DataBindings.Add("IntValue", cratesSection, "Radar");
nudArmageddon.DataBindings.Add("IntValue", cratesSection, "Armageddon");
nudSuper.DataBindings.Add("IntValue", cratesSection, "Super");
nudDensity.DataBindings.Add("IntValue", cratesSection, "Density");
nudIonFactor.DataBindings.Add("IntValue", cratesSection, "IonFactor");
nudCrateTimer.DataBindings.Add("IntValue", cratesSection, "CrateTimer");
AddDataBindings();
}
private void CrateSettings_Load(Object sender, EventArgs e)
private void btnDefaults_Click(Object sender, EventArgs e)
{
AdjustPanelSize(this, tableLayoutPanel1);
}
private void AdjustPanelSize(ScrollableControl panel, TableLayoutPanel tableLayoutPanel)
{
int maxX = 0;
int maxY = 0;
foreach (Control c in tableLayoutPanel.Controls)
if (DialogResult.Yes == MessageBox.Show("This will reset all crate values to their game defaults. Are you sure you want to continue?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1))
{
maxX = Math.Max(maxX, c.Location.X + c.Width);
maxY = Math.Max(maxY, c.Location.Y + c.Height);
ResetToDefaultValues();
}
panel.AutoScrollMinSize = new Size(maxX, maxY);
}
private void ResetToDefaultValues()
{
RemoveDataBindings();
SoleSurvivor.CratesSection cratesSection = new SoleSurvivor.CratesSection();
cratesSection.SetDefault();
cratesSettings.SetValues(cratesSection);
AddDataBindings();
}
private void AddDataBindings()
{
nudAddStrength.DataBindings.Add("IntValue", cratesSettings, "AddStrength");
nudAddWeapon.DataBindings.Add("IntValue", cratesSettings, "AddWeapon");
nudAddSpeed.DataBindings.Add("IntValue", cratesSettings, "AddSpeed");
nudRapidReload.DataBindings.Add("IntValue", cratesSettings, "RapidReload");
nudAddRange.DataBindings.Add("IntValue", cratesSettings, "AddRange");
nudHeal.DataBindings.Add("IntValue", cratesSettings, "Heal");
nudBomb.DataBindings.Add("IntValue", cratesSettings, "Bomb");
nudStealth.DataBindings.Add("IntValue", cratesSettings, "Stealth");
nudTeleport.DataBindings.Add("IntValue", cratesSettings, "Teleport");
nudKill.DataBindings.Add("IntValue", cratesSettings, "Kill");
nudUncloakAll.DataBindings.Add("IntValue", cratesSettings, "UncloakAll");
nudReshroud.DataBindings.Add("IntValue", cratesSettings, "Reshroud");
nudUnshroud.DataBindings.Add("IntValue", cratesSettings, "Unshroud");
nudRadar.DataBindings.Add("IntValue", cratesSettings, "Radar");
nudArmageddon.DataBindings.Add("IntValue", cratesSettings, "Armageddon");
nudSuper.DataBindings.Add("IntValue", cratesSettings, "Super");
nudDensity.DataBindings.Add("IntValue", cratesSettings, "Density");
nudIonFactor.DataBindings.Add("IntValue", cratesSettings, "IonFactor");
nudCrateTimer.DataBindings.Add("IntValue", cratesSettings, "CrateTimer");
}
private void RemoveDataBindings()
{
nudAddStrength.DataBindings.Clear();
nudAddWeapon.DataBindings.Clear();
nudAddSpeed.DataBindings.Clear();
nudRapidReload.DataBindings.Clear();
nudAddRange.DataBindings.Clear();
nudHeal.DataBindings.Clear();
nudBomb.DataBindings.Clear();
nudStealth.DataBindings.Clear();
nudTeleport.DataBindings.Clear();
nudKill.DataBindings.Clear();
nudUncloakAll.DataBindings.Clear();
nudReshroud.DataBindings.Clear();
nudUnshroud.DataBindings.Clear();
nudRadar.DataBindings.Clear();
nudArmageddon.DataBindings.Clear();
nudSuper.DataBindings.Clear();
nudDensity.DataBindings.Clear();
nudIonFactor.DataBindings.Clear();
nudCrateTimer.DataBindings.Clear();
}
}
}

View File

@ -120,15 +120,17 @@ namespace MobiusEditor.Controls
string[] filteredEvents;
string[] filteredActions;
Boolean isAircraft = obj is Unit un && un.Type.IsAircraft;
Boolean isOnMap = true;
switch (obj)
{
case Infantry infantry:
case Unit unit:
items = Plugin.Map.FilterUnitTriggers().Select(t => t.Name).Distinct().ToArray();
filteredEvents = isAircraft ? new string[0] : Plugin.Map.EventTypes.Where(ev => Plugin.Map.UnitEventTypes.Contains(ev)).Distinct().ToArray();
filteredActions = isAircraft ? new string[0] : Plugin.Map.ActionTypes.Where(ac => Plugin.Map.UnitActionTypes.Contains(ac)).Distinct().ToArray();
filteredEvents = Plugin.Map.EventTypes.Where(ev => Plugin.Map.UnitEventTypes.Contains(ev)).Distinct().ToArray();
filteredActions = Plugin.Map.ActionTypes.Where(ac => Plugin.Map.UnitActionTypes.Contains(ac)).Distinct().ToArray();
break;
case Building building:
isOnMap = building.IsPrebuilt;
items = Plugin.Map.FilterStructureTriggers().Select(t => t.Name).Distinct().ToArray();
filteredEvents = Plugin.Map.EventTypes.Where(ac => Plugin.Map.StructureEventTypes.Contains(ac)).Distinct().ToArray();
filteredActions = Plugin.Map.ActionTypes.Where(ac => Plugin.Map.StructureActionTypes.Contains(ac)).Distinct().ToArray();
@ -143,7 +145,7 @@ namespace MobiusEditor.Controls
items = Trigger.None.Yield().Concat(Plugin.Map.Triggers.Select(t => t.Name).Where(t => allowedTriggers.Contains(t)).Distinct()).ToArray();
int selectIndex = selected == null ? 0 : Enumerable.Range(0, items.Length).FirstOrDefault(x => String.Equals(items[x], selected, StringComparison.InvariantCultureIgnoreCase));
triggerComboBox.DataSource = items;
triggerComboBox.Enabled = !isAircraft;
triggerComboBox.Enabled = !isAircraft && isOnMap;
triggerToolTip = Map.MakeAllowedTriggersToolTip(filteredEvents, filteredActions);
if (obj != null)
{
@ -163,12 +165,10 @@ namespace MobiusEditor.Controls
prebuiltCheckBox.DataBindings.Clear();
sellableCheckBox.DataBindings.Clear();
rebuildCheckBox.DataBindings.Clear();
if (obj == null)
{
return;
}
switch (obj)
{
case Infantry infantry:
@ -177,7 +177,6 @@ namespace MobiusEditor.Controls
directionComboBox.DataSource = Plugin.Map.DirectionTypes
.Where(t => t.Facing != FacingType.None)
.Select(t => new TypeItem<DirectionType>(t.Name, t)).ToArray();
missionComboBox.DataBindings.Add("SelectedItem", obj, "Mission");
missionLabel.Visible = missionComboBox.Visible = true;
basePriorityLabel.Visible = basePriorityNud.Visible = false;
@ -243,7 +242,6 @@ namespace MobiusEditor.Controls
}
break;
}
houseComboBox.DataBindings.Add("SelectedValue", obj, "House");
strengthNud.DataBindings.Add("Value", obj, "Strength");
directionComboBox.DataBindings.Add("SelectedValue", obj, "Direction");

View File

@ -46,6 +46,7 @@ namespace MobiusEditor.Controls
switch (plugin.GameType)
{
case GameType.TiberianDawn:
case GameType.SoleSurvivor:
maxInfantryNud.Visible = maxInfantryLbl.Visible = false;
maxVesselsNud.Visible = maxVesselsLbl.Visible = false;
techLevelNud.Visible = techLevelLbl.Visible = false;

View File

@ -587,6 +587,7 @@ namespace MobiusEditor.Dialogs
0,
0,
0});
this.nudInitNum.IntValue = 0;
this.nudInitNum.Location = new System.Drawing.Point(85, 198);
this.nudInitNum.Margin = new System.Windows.Forms.Padding(2);
this.nudInitNum.Maximum = new decimal(new int[] {
@ -610,6 +611,7 @@ namespace MobiusEditor.Dialogs
0,
0,
0});
this.maxAllowedNud.IntValue = 0;
this.maxAllowedNud.Location = new System.Drawing.Point(85, 222);
this.maxAllowedNud.Margin = new System.Windows.Forms.Padding(2);
this.maxAllowedNud.Maximum = new decimal(new int[] {
@ -633,6 +635,7 @@ namespace MobiusEditor.Dialogs
0,
0,
0});
this.nudFear.IntValue = 0;
this.nudFear.Location = new System.Drawing.Point(85, 246);
this.nudFear.Margin = new System.Windows.Forms.Padding(2);
this.nudFear.Maximum = new decimal(new int[] {
@ -716,6 +719,7 @@ namespace MobiusEditor.Dialogs
0,
0,
0});
this.nudRecruitPriority.IntValue = 0;
this.nudRecruitPriority.Location = new System.Drawing.Point(85, 174);
this.nudRecruitPriority.Margin = new System.Windows.Forms.Padding(2);
this.nudRecruitPriority.Maximum = new decimal(new int[] {

View File

@ -32,6 +32,7 @@ namespace MobiusEditor.Dialogs
private Bitmap infoImage;
private string triggerToolTip;
private const int maxLength = 8;
private readonly IGamePlugin plugin;
private readonly int maxTeams;
private readonly IEnumerable<ITechnoType> technoTypes;
@ -394,17 +395,6 @@ namespace MobiusEditor.Dialogs
private void TeamTypesListView_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
int maxLength = int.MaxValue;
switch (plugin.GameType)
{
case GameType.TiberianDawn:
case GameType.SoleSurvivor:
maxLength = 8;
break;
case GameType.RedAlert:
maxLength = 23;
break;
}
String curName = e.Label;
if (string.IsNullOrEmpty(curName))
{

View File

@ -25,6 +25,7 @@ namespace MobiusEditor.Dialogs
{
public partial class TriggersDialog : Form
{
private const int maxLength = 4;
private readonly IGamePlugin plugin;
private readonly int maxTriggers;
@ -311,17 +312,6 @@ namespace MobiusEditor.Dialogs
private void triggersListView_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
int maxLength = int.MaxValue;
switch (plugin.GameType)
{
case GameType.TiberianDawn:
case GameType.SoleSurvivor:
maxLength = 4;
break;
case GameType.RedAlert:
maxLength = 23;
break;
}
String curName = e.Label;
if (string.IsNullOrEmpty(curName))
{

View File

@ -306,12 +306,7 @@ namespace MobiusEditor
string[] modPaths = null;
if (ModPaths != null)
{
GameType modGameType = nmd.GameType;
if (modGameType == GameType.SoleSurvivor)
{
modGameType = GameType.TiberianDawn;
}
ModPaths.TryGetValue(modGameType, out modPaths);
ModPaths.TryGetValue(nmd.GameType, out modPaths);
}
Globals.TheTextureManager.ExpandModPaths = modPaths;
Globals.TheTextureManager.Reset();
@ -896,11 +891,7 @@ namespace MobiusEditor
{
case FileType.INI:
{
gameType = File.Exists(Path.ChangeExtension(loadFilename, ".bin")) ? GameType.TiberianDawn : GameType.RedAlert;
if (gameType == GameType.RedAlert && !RedAlert.GamePlugin.CheckForRAMap(loadFilename, fileType))
{
gameType = GameType.TiberianDawn;
}
gameType = RedAlert.GamePlugin.CheckForRAMap(loadFilename, fileType) ? GameType.RedAlert : GameType.TiberianDawn;
break;
}
case FileType.BIN:
@ -933,9 +924,11 @@ namespace MobiusEditor
}
#endif
}
bool isTdMegaMap = false;
if (gameType == GameType.TiberianDawn)
{
if (SoleSurvivor.GamePlugin.CheckForSSmap(loadFilename, fileType))
isTdMegaMap = TiberianDawn.GamePlugin.CheckForMegamap(loadFilename, fileType);
if (isTdMegaMap && SoleSurvivor.GamePlugin.CheckForSSmap(loadFilename, fileType))
{
gameType = GameType.SoleSurvivor;
}
@ -953,12 +946,7 @@ namespace MobiusEditor
string[] modPaths = null;
if (ModPaths != null)
{
GameType modGameType = gameType;
if (modGameType == GameType.SoleSurvivor)
{
modGameType = GameType.TiberianDawn;
}
ModPaths.TryGetValue(modGameType, out modPaths);
ModPaths.TryGetValue(gameType, out modPaths);
}
Globals.TheTextureManager.ExpandModPaths = modPaths;
Globals.TheTextureManager.Reset();
@ -968,19 +956,14 @@ namespace MobiusEditor
switch (gameType)
{
case GameType.TiberianDawn:
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
bool isMegaMap = TiberianDawn.GamePlugin.CheckForMegamap(loadFilename, fileType);
plugin = new TiberianDawn.GamePlugin(isMegaMap, this);
}
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML");
plugin = new TiberianDawn.GamePlugin(isTdMegaMap, this);
break;
case GameType.RedAlert:
{
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
plugin = new RedAlert.GamePlugin(this);
}
Globals.TheTeamColorManager.Reset();
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
plugin = new RedAlert.GamePlugin(this);
break;
case GameType.SoleSurvivor:
Globals.TheTeamColorManager.Reset();
@ -1688,11 +1671,6 @@ namespace MobiusEditor
{
return;
}
if (!SteamworksUGC.IsInit)
{
MessageBox.Show("Steam interface is not initialized. To enable Workshop publishing, log into Steam and restart the editor.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (plugin.GameType == GameType.SoleSurvivor)
{
MessageBox.Show("Sole Survivor maps cannot be published to Steam; they are not usable by the C&C Remastered Collection.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
@ -1707,6 +1685,11 @@ namespace MobiusEditor
MessageBox.Show("Tiberian Dawn megamaps cannot be published to Steam; they are not usable by the C&C Remastered Collection without modding, and may cause issues on the official servers.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!SteamworksUGC.IsInit)
{
MessageBox.Show("Steam interface is not initialized. To enable Workshop publishing, log into Steam and restart the editor.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (!PromptSaveMap())
{
return;

View File

@ -1070,7 +1070,7 @@ namespace MobiusEditor.Model
{
continue;
}
MapLayerFlag layer = MapLayerFlag.None;
MapLayerFlag layer;
if (overlay.Type.IsResource)
{
layer = MapLayerFlag.Resources;

View File

@ -14,6 +14,7 @@
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using System;
using System.Collections.Generic;
using System.Linq;
namespace MobiusEditor.Model
{
@ -29,7 +30,7 @@ namespace MobiusEditor.Model
{
ID = id;
Name = name;
Tilesets = tilesets;
Tilesets = tilesets.Distinct();
}
public override bool Equals(object obj)

View File

@ -117,7 +117,16 @@ namespace MobiusEditor
}
#endif
// Check if any mods are allowed to override the default stuff to load.
Dictionary<GameType, string[]> modPaths = GetModPaths(gameId, Properties.Settings.Default.ModsToLoad);
Dictionary<GameType, string> pathsToLoad = new Dictionary<GameType, string>();
pathsToLoad.Add(GameType.TiberianDawn, Properties.Settings.Default.ModsToLoadTD);
pathsToLoad.Add(GameType.RedAlert, Properties.Settings.Default.ModsToLoadRA);
pathsToLoad.Add(GameType.SoleSurvivor, Properties.Settings.Default.ModsToLoadSS);
Dictionary<GameType, string[]> modPaths = new Dictionary<GameType, string[]>();
const string tdModFolder = "Tiberian_Dawn";
const string raModFolder = "Red_Alert";
modPaths.Add(GameType.TiberianDawn, GetModPaths(gameId, Properties.Settings.Default.ModsToLoadTD, tdModFolder, "TD"));
modPaths.Add(GameType.RedAlert, GetModPaths(gameId, Properties.Settings.Default.ModsToLoadRA, raModFolder, "RA"));
modPaths.Add(GameType.SoleSurvivor, GetModPaths(gameId, Properties.Settings.Default.ModsToLoadSS, tdModFolder, "TD"));
// Initialize texture, tileset, team color, and game text managers
Globals.TheTextureManager = new TextureManager(Globals.TheMegafileManager);
Globals.TheTilesetManager = new TilesetManager(Globals.TheMegafileManager, Globals.TheTextureManager, Globals.TilesetsXMLPath, Globals.TexturesPath);
@ -133,13 +142,8 @@ namespace MobiusEditor
// Initialize Steam if this is a Steam build
if (SteamworksUGC.IsSteamBuild)
{
if (!SteamworksUGC.Init())
{
#if !DEVELOPER
//MessageBox.Show("Unable to initialize Steam interface.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
//return;
#endif
}
// Ignore result from this.
SteamworksUGC.Init();
}
if (Properties.Settings.Default.ShowInviteWarning)
{
@ -174,21 +178,17 @@ namespace MobiusEditor
Globals.TheMegafileManager.Dispose();
}
private static Dictionary<GameType, string[]> GetModPaths(string gameId, string modstoLoad)
private static string[] GetModPaths(string gameId, string modstoLoad, string modFolder, string modIdentifier)
{
Regex numbersOnly = new Regex("^\\d+$");
Regex modregex = new Regex("\"game_type\"\\s*:\\s*\"((RA)|(TD))\"");
const string tdModFolder = "Tiberian_Dawn";
const string raModFolder = "Red_Alert";
Regex modregex = new Regex("\"game_type\"\\s*:\\s*\""+ modIdentifier + "\"");
const string contentFile = "ccmod.json";
string modsFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "CnCRemastered", "Mods");
string[] steamLibraryFolders = SteamAssist.GetLibraryFoldersForAppId(gameId);
string[] mods = (modstoLoad ?? String.Empty).Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
List<string> modPathsRa = new List<string>();
List<string> modPathsTd = new List<string>();
List<string> modPaths = new List<string>();
for (int i = 0; i < mods.Length; ++i)
{
List<string> modList;
string modDef = mods[i].Trim();
if (String.IsNullOrEmpty(modDef))
{
@ -201,48 +201,22 @@ namespace MobiusEditor
addonModPath = SteamAssist.TryGetSteamModFolder(gameId, modDef, null, contentFile); // addonModName);
if (addonModPath != null)
{
switch (CheckAddonPathModType(addonModPath, contentFile, modregex, 1))
if (CheckAddonPathModType(addonModPath, contentFile, modregex))
{
case "TD":
modList = modPathsTd;
break;
case "RA":
modList = modPathsRa;
break;
default:
continue;
}
modList.Add(addonModPath);
modPaths.Add(addonModPath);
}
}
// don't bother checking more on a numbers-only entry.
continue;
}
// Lookup by folder name
bool isTDMod = modDef.StartsWith(tdModFolder, StringComparison.OrdinalIgnoreCase);
bool isRAMod = modDef.StartsWith(raModFolder, StringComparison.OrdinalIgnoreCase);
if (!isTDMod && !isRAMod)
addonModPath = Path.Combine(modsFolder, modFolder, modDef);
if (CheckAddonPathModType(addonModPath, contentFile, modregex))
{
modPaths.Add(addonModPath);
// Found in local mods; don't check Steam ones.
continue;
}
String expectedModType = isTDMod ? "TD" : "RA";
modList = isTDMod ? modPathsTd : modPathsRa;
string actualModFolder = modDef.Substring(isTDMod ? tdModFolder.Length : raModFolder.Length);
// check if the trimmed-off part was indeed the whole folder name.
if (!actualModFolder.StartsWith("\\") && !actualModFolder.StartsWith("/"))
{
continue;
}
actualModFolder = actualModFolder.Trim('\\', '/');
// C&C Remastered mods can only be one path deep.
if (actualModFolder.Contains('\\') || actualModFolder.Contains('/'))
{
continue;
}
addonModPath = Path.Combine(modsFolder, modDef);
if (CheckAddonPathModType(addonModPath, contentFile, modregex, 1) == expectedModType)
{
modList.Add(addonModPath);
}
// try to find mod in steam library.
foreach (string libFolder in steamLibraryFolders)
{
@ -251,39 +225,35 @@ namespace MobiusEditor
{
continue;
}
foreach (string modFolder in Directory.GetDirectories(modPath))
foreach (string modPth in Directory.GetDirectories(modPath))
{
addonModPath = Path.Combine(modFolder, actualModFolder);
if (CheckAddonPathModType(addonModPath, contentFile, modregex, 1) == expectedModType)
addonModPath = Path.Combine(modPth, modDef);
if (CheckAddonPathModType(addonModPath, contentFile, modregex))
{
modList.Add(addonModPath);
modPaths.Add(addonModPath);
break;
}
}
}
}
Dictionary<GameType, string[]> finalModPaths = new Dictionary<GameType, string[]>();
finalModPaths.Add(GameType.TiberianDawn, modPathsTd.Distinct(StringComparer.CurrentCultureIgnoreCase).ToArray());
finalModPaths.Add(GameType.RedAlert, modPathsRa.Distinct(StringComparer.CurrentCultureIgnoreCase).ToArray());
return finalModPaths;
return modPaths.Distinct(StringComparer.CurrentCultureIgnoreCase).ToArray();
}
private static string CheckAddonPathModType(string addonModPath, string contentFile, Regex modregex, int group)
private static bool CheckAddonPathModType(string addonModPath, string contentFile, Regex modregex)
{
try
{
string checkPath = Path.Combine(addonModPath, contentFile);
if (!File.Exists(checkPath))
{
return null;
return false;
}
string ccModDefContents = File.ReadAllText(checkPath);
MatchCollection mc = modregex.Matches(ccModDefContents);
return mc.Count == 0 ? null : mc[0].Groups[group].Value;
return modregex.IsMatch(ccModDefContents);
}
catch
{
return null;
return false;
}
}

View File

@ -25,10 +25,28 @@ namespace MobiusEditor.Properties {
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2844969675;Tiberian_Dawn\\ConcretePavementTD")]
public string ModsToLoad {
[global::System.Configuration.DefaultSettingValueAttribute("2844969675;ConcretePavementTD")]
public string ModsToLoadTD {
get {
return ((string)(this["ModsToLoad"]));
return ((string)(this["ModsToLoadTD"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string ModsToLoadRA {
get {
return ((string)(this["ModsToLoadRA"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("2844969675;ConcretePavementTD")]
public string ModsToLoadSS {
get {
return ((string)(this["ModsToLoadSS"]));
}
}

View File

@ -2,8 +2,14 @@
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MobiusEditor.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="ModsToLoad" Type="System.String" Scope="Application">
<Value Profile="(Default)">2844969675;Tiberian_Dawn\ConcretePavementTD</Value>
<Setting Name="ModsToLoadTD" Type="System.String" Scope="Application">
<Value Profile="(Default)">2844969675;ConcretePavementTD</Value>
</Setting>
<Setting Name="ModsToLoadRA" Type="System.String" Scope="Application">
<Value Profile="(Default)" />
</Setting>
<Setting Name="ModsToLoadSS" Type="System.String" Scope="Application">
<Value Profile="(Default)">2844969675;ConcretePavementTD</Value>
</Setting>
<Setting Name="MapScale" Type="System.Double" Scope="Application">
<Value Profile="(Default)">0.5</Value>

View File

@ -460,7 +460,7 @@ namespace MobiusEditor.RedAlert
ini.Parse(reader);
}
forceSingle = SinglePlayRegex.IsMatch(Path.GetFileNameWithoutExtension(path));
errors.AddRange(LoadINI(ini, forceSingle));
errors.AddRange(LoadINI(ini, forceSingle, ref modified));
}
break;
case FileType.MEG:
@ -478,18 +478,13 @@ namespace MobiusEditor.RedAlert
{
ini.Parse(reader);
}
errors.AddRange(LoadINI(ini, false));
errors.AddRange(LoadINI(ini, false, ref modified));
}
}
break;
default:
throw new NotSupportedException("Unsupported filetype.");
}
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
@ -498,7 +493,7 @@ namespace MobiusEditor.RedAlert
}
}
private IEnumerable<string> LoadINI(INI ini, bool forceSoloMission)
private IEnumerable<string> LoadINI(INI ini, bool forceSoloMission, ref bool modified)
{
var errors = new List<string>();
Map.BeginUpdate();
@ -570,6 +565,10 @@ namespace MobiusEditor.RedAlert
{
try
{
if (Key.Length > 8)
{
errors.Add(string.Format("TeamType '{0}' has a name that is longer than 8 characters. This will not be corrected by the loading process, but should be addressed, since it can make the teams fail to read correctly, and might even crash the game.", Key));
}
var teamType = new TeamType { Name = Key };
var tokens = Value.Split(',').ToList();
teamType.House = Map.HouseTypes.Where(t => t.Equals(sbyte.Parse(tokens[0]))).FirstOrDefault(); tokens.RemoveAt(0);
@ -598,17 +597,20 @@ namespace MobiusEditor.RedAlert
{
errors.Add(string.Format("Team '{0}' contains expansion unit '{0}', but expansion units not enabled; enabling expansion units.", Key, type.Name));
Map.BasicSection.ExpansionEnabled = aftermathEnabled = true;
modified = true;
}
teamType.Classes.Add(new TeamTypeClass { Type = type, Count = count });
}
else
{
errors.Add(string.Format("Team '{0}' references unknown class '{1}'.", Key, classTokens[0]));
modified = true;
}
}
else
{
errors.Add(string.Format("Team '{0}' has wrong number of tokens for class index {1} (expecting 2).", Key, i));
modified = true;
}
}
var numMissions = int.Parse(tokens[0]); tokens.RemoveAt(0);
@ -622,6 +624,7 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(string.Format("Team '{0}' has wrong number of tokens for mission index {1} (expecting 2).", Key, i));
modified = true;
}
}
teamTypes.Add(teamType);
@ -629,10 +632,12 @@ namespace MobiusEditor.RedAlert
catch (Exception ex)
{
errors.Add(string.Format("Teamtype '{0}' has errors and can't be parsed: {1}.", Key, ex.Message));
modified = true;
}
}
}
var triggersSection = ini.Sections.Extract("Trigs");
List<Trigger> triggers = new List<Trigger>();
if (triggersSection != null)
{
foreach (var (Key, Value) in triggersSection)
@ -642,6 +647,10 @@ namespace MobiusEditor.RedAlert
var tokens = Value.Split(',');
if (tokens.Length == 18)
{
if (Key.Length > 4)
{
errors.Add(string.Format("Trigger '{0}' has a name that is longer than 4 characters. This will not be corrected by the loading process, but should be addressed, since it can make the triggers fail to read correctly and link to objects and cell triggers, and might even crash the game.", Key));
}
var trigger = new Trigger { Name = Key };
trigger.PersistentType = (TriggerPersistentType)int.Parse(tokens[0]);
trigger.House = Map.HouseTypes.Where(t => t.Equals(sbyte.Parse(tokens[1]))).FirstOrDefault()?.Name ?? "None";
@ -725,20 +734,22 @@ namespace MobiusEditor.RedAlert
fixEvent(trigger.Event2);
fixAction(trigger.Action1);
fixAction(trigger.Action2);
Map.Triggers.Add(trigger);
triggers.Add(trigger);
}
else
{
errors.Add(string.Format("Trigger '{0}' has too few tokens (expecting 18).", Key));
modified = true;
}
}
catch (Exception ex)
{
errors.Add(string.Format("Trigger '{0}' has errors and can't be parsed: {1}.", Key, ex.Message));
modified = true;
}
}
}
HashSet<string> checkTrigs = Trigger.None.Yield().Concat(Map.Triggers.Select(t => t.Name)).ToHashSet(StringComparer.InvariantCultureIgnoreCase);
HashSet<string> checkTrigs = Trigger.None.Yield().Concat(triggers.Select(t => t.Name)).ToHashSet(StringComparer.InvariantCultureIgnoreCase);
HashSet<string> checkCellTrigs = Map.FilterCellTriggers().Select(t => t.Name).ToHashSet(StringComparer.InvariantCultureIgnoreCase);
HashSet<string> checkUnitTrigs = Trigger.None.Yield().Concat(Map.FilterUnitTriggers().Select(t => t.Name)).ToHashSet(StringComparer.InvariantCultureIgnoreCase);
HashSet<string> checkStrcTrigs = Trigger.None.Yield().Concat(Map.FilterStructureTriggers().Select(t => t.Name)).ToHashSet(StringComparer.InvariantCultureIgnoreCase);
@ -768,6 +779,7 @@ namespace MobiusEditor.RedAlert
if (templateType == null && typeValue != 0xFFFF)
{
errors.Add(String.Format("Unknown template value {0:X4} at cell [{1},{2}]; clearing.", typeValue, x, y));
modified = true;
}
else if (templateType != null)
{
@ -785,6 +797,7 @@ namespace MobiusEditor.RedAlert
if (!clearedOldClear)
{
errors.Add(String.Format("Use of obsolete version of 'Clear' terrain detected; clearing."));
modified = true;
clearedOldClear = true;
}
templateType = null;
@ -793,6 +806,7 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(String.Format("Template '{0}' at cell [{1},{2}] is not available in the set theater; clearing.", templateType.Name.ToUpper(), x, y));
modified = true;
templateType = null;
}
}
@ -820,6 +834,7 @@ namespace MobiusEditor.RedAlert
{
// This is an old map. Clear any 255 tile.
errors.Add(String.Format("Use of obsolete version of 'Clear' terrain detected; clearing."));
modified = true;
for (var y = 0; y < height; ++y)
{
for (var x = 0; x < width; ++x)
@ -848,10 +863,12 @@ namespace MobiusEditor.RedAlert
if (iconValue >= templateType.NumIcons)
{
errors.Add(String.Format("Template '{0}' at cell [{1},{2}] has an icon set ({3}) that is outside its icons range; clearing.", templateType.Name.ToUpper(), x, y, iconValue));
modified = true;
}
else if (!isRandom && templateType.IconMask != null && !templateType.IconMask[iconValue / templateType.IconWidth, iconValue % templateType.IconWidth])
{
errors.Add(String.Format("Template '{0}' at cell [{1},{2}] has an icon set ({3}) that is not part of its placeable cells; clearing.", templateType.Name.ToUpper(), x, y, iconValue));
modified = true;
}
else if (templateType != TemplateTypes.Clear)
{
@ -880,6 +897,7 @@ namespace MobiusEditor.RedAlert
if (!int.TryParse(Key, out cell))
{
errors.Add(string.Format("Cell for terrain cannot be parsed. Key: '{0}', value: '{1}'; skipping.", Key, Value));
modified = true;
continue;
}
var name = Value.Split(',')[0];
@ -889,6 +907,7 @@ namespace MobiusEditor.RedAlert
if (Globals.FilterTheaterObjects && terrainType.Theaters != null && !terrainType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Terrain '{0}' is not available in the set theater; skipping.", terrainType.Name));
modified = true;
continue;
}
Terrain newTerr = new Terrain
@ -901,43 +920,51 @@ namespace MobiusEditor.RedAlert
//if (!checkTrigs.Contains(newTerr.Trigger))
//{
// errors.Add(string.Format("Terrain '{0}' links to unknown trigger '{1}'; clearing trigger..", terrainType, newTerr.Trigger));
// modified = true;
// newTerr.Trigger = Trigger.None;
//}
//else if (!checkTerrTrigs.Contains(Value))
//{
// errors.Add(string.Format("Terrain '{0}' links to trigger '{1}' which does not contain an event or action applicable to terrain; clearing trigger.", terrainType, newTerr.Trigger));
// modified = true;
// newTerr.Trigger = Trigger.None;
//}
}
else
{
var techno = Map.FindBlockingObject(cell, terrainType, out int blockingCell);
string reportCell = blockingCell == -1 ? cell.ToString() : "<unknown>";
string reportCell = blockingCell == -1 ? "<unknown>" : cell.ToString();
if (techno is Building building)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps structure '{2}' in cell {3}; skipping.", terrainType.Name, cell, building.Type.Name, reportCell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps overlay '{2}' in cell {3}; skipping.", terrainType.Name, cell, overlay.Type.Name, reportCell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps terrain '{2}' in cell {3}; skipping.", terrainType.Name, cell, terrain.Type.Name, reportCell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps infantry in cell {2}; skipping.", terrainType.Name, cell, reportCell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps unit '{2}' in cell {3}; skipping.", terrainType.Name, cell, unit.Type.Name, reportCell));
modified = true;
}
else
{
if (blockingCell != -1)
{
errors.Add(string.Format("Terrain '{0}' placed on cell {1} overlaps unknown techno in cell {2}; skipping.", terrainType.Name, cell, blockingCell));
modified = true;
}
else
{
@ -949,6 +976,7 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(string.Format("Terrain '{0}' references unknown terrain.", name));
modified = true;
}
}
}
@ -971,6 +999,7 @@ namespace MobiusEditor.RedAlert
if (Globals.FilterTheaterObjects && overlayType.Theaters != null && !overlayType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Overlay '{0}' is not available in the set theater; skipping.", overlayType.Name));
modified = true;
continue;
}
Map.Overlay[i] = new Overlay { Type = overlayType, Icon = 0 };
@ -978,6 +1007,7 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(string.Format("Overlay ID {0} references unknown overlay.", overlayId));
modified = true;
}
}
}
@ -992,6 +1022,7 @@ namespace MobiusEditor.RedAlert
if (!int.TryParse(Key, out cell))
{
errors.Add(string.Format("Cell for Smudge cannot be parsed. Key: '{0}', value: '{1}'; skipping.", Key, Value));
modified = true;
continue;
}
var tokens = Value.Split(',');
@ -1005,11 +1036,13 @@ namespace MobiusEditor.RedAlert
if (Globals.FilterTheaterObjects && smudgeType.Theaters != null && !smudgeType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Smudge '{0}' is not available in the set theater; skipping.", smudgeType.Name));
modified = true;
continue;
}
if (badCrater)
{
errors.Add(string.Format("Smudge '{0}' does not function correctly in maps. Correcting to '{1}'.", tokens[0], smudgeType.Name));
modified = true;
}
int icon = 0;
if (smudgeType.Icons > 1 && int.TryParse(tokens[2], out icon))
@ -1038,11 +1071,13 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(string.Format("Smudge '{0}' references unknown smudge.", tokens[0]));
modified = true;
}
}
else
{
errors.Add(string.Format("Smudge on cell '{0}' has wrong number of tokens (expecting 3).", Key));
modified = true;
}
}
}
@ -1058,29 +1093,34 @@ namespace MobiusEditor.RedAlert
if (unitType == null)
{
errors.Add(string.Format("Unit '{0}' references unknown unit.", tokens[1]));
modified = true;
continue;
}
if (!aftermathEnabled && unitType.IsExpansionUnit)
{
errors.Add(string.Format("Expansion unit '{0}' encountered, but expansion units not enabled; enabling expansion units.", unitType.Name));
modified = true;
Map.BasicSection.ExpansionEnabled = aftermathEnabled = true;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for unit '{0}' cannot be parsed; value: '{1}'; skipping.", unitType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for unit '{0}' cannot be parsed; value: '{1}'; skipping.", unitType.Name, tokens[3]));
modified = true;
continue;
}
int dirValue;
if (!int.TryParse(tokens[4], out dirValue))
{
errors.Add(string.Format("Direction for unit '{0}' cannot be parsed; value: '{1}'; skipping.", unitType.Name, tokens[4]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1098,11 +1138,13 @@ namespace MobiusEditor.RedAlert
if (!checkTrigs.Contains(tokens[6]))
{
errors.Add(string.Format("Unit '{0}' links to unknown trigger '{1}'; clearing trigger.", unitType.Name, tokens[6]));
modified = true;
newUnit.Trigger = Trigger.None;
}
else if (!checkUnitTrigs.Contains(tokens[6]))
{
errors.Add(string.Format("Unit '{0}' links to trigger '{1}' which does not contain an event or action applicable to units; clearing trigger.", unitType.Name, tokens[6]));
modified = true;
newUnit.Trigger = Trigger.None;
}
}
@ -1112,26 +1154,32 @@ namespace MobiusEditor.RedAlert
if (techno is Building building)
{
errors.Add(string.Format("Unit '{0}' overlaps structure '{1}' in cell {2}; skipping.", unitType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Unit '{0}' overlaps overlay '{1}' in cell {2}; skipping.", unitType.Name, overlay.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Unit '{0}' overlaps terrain '{1}' in cell {2}; skipping.", unitType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Unit '{0}' overlaps infantry in cell {1}; skipping.", unitType.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Unit '{0}' overlaps unit '{1}' in cell {2}; skipping.", unitType.Name, unit.Type.Name, cell));
modified = true;
}
else
{
errors.Add(string.Format("Unit '{0}' overlaps unknown techno in cell {1}; skipping.", unitType.Name, cell));
modified = true;
}
}
}
@ -1140,10 +1188,12 @@ namespace MobiusEditor.RedAlert
if (tokens.Length < 2)
{
errors.Add(string.Format("Unit entry '{0}' has wrong number of tokens (expecting 7).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Unit '{0}' has wrong number of tokens (expecting 7).", tokens[1]));
modified = true;
}
}
}
@ -1162,24 +1212,28 @@ namespace MobiusEditor.RedAlert
if (aircraftType == null)
{
errors.Add(string.Format("Aircraft '{0}' references unknown aircraft.", tokens[1]));
modified = true;
continue;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", aircraftType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", aircraftType.Name, tokens[3]));
modified = true;
continue;
}
int dirValue;
if (!int.TryParse(tokens[4], out dirValue))
{
errors.Add(string.Format("Direction for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", aircraftType.Name, tokens[4]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1197,26 +1251,32 @@ namespace MobiusEditor.RedAlert
if (techno is Building building)
{
errors.Add(string.Format("Aircraft '{0}' overlaps structure '{1}' in cell {2}; skipping.", aircraftType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Aircraft '{0}' overlaps overlay '{1}' in cell {2}; skipping.", aircraftType.Name, overlay.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Aircraft '{0}' overlaps terrain '{1}' in cell {2}; skipping.", aircraftType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Aircraft '{0}' overlaps infantry in cell {1}; skipping.", aircraftType.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Aircraft '{0}' overlaps unit '{1}' in cell {2}; skipping.", aircraftType.Name, unit.Type.Name, cell));
modified = true;
}
else
{
errors.Add(string.Format("Aircraft '{0}' overlaps unknown techno in cell {1}; skipping.", aircraftType.Name, cell));
modified = true;
}
}
}
@ -1225,10 +1285,12 @@ namespace MobiusEditor.RedAlert
if (tokens.Length < 2)
{
errors.Add(string.Format("Aircraft entry '{0}' has wrong number of tokens (expecting 6).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Aircraft '{0}' has wrong number of tokens (expecting 6).", tokens[1]));
modified = true;
}
}
}
@ -1245,24 +1307,28 @@ namespace MobiusEditor.RedAlert
if (vesselType == null)
{
errors.Add(string.Format("Ship '{0}' references unknown ship.", tokens[1]));
modified = true;
continue;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", vesselType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", vesselType.Name, tokens[3]));
modified = true;
continue;
}
int dirValue;
if (!int.TryParse(tokens[4], out dirValue))
{
errors.Add(string.Format("Direction for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", vesselType.Name, tokens[4]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1280,11 +1346,13 @@ namespace MobiusEditor.RedAlert
if (!checkTrigs.Contains(tokens[6]))
{
errors.Add(string.Format("Ship '{0}' links to unknown trigger '{1}'; clearing trigger.", vesselType.Name, tokens[6]));
modified = true;
newShip.Trigger = Trigger.None;
}
else if (!checkUnitTrigs.Contains(tokens[6]))
{
errors.Add(string.Format("Ship '{0}' links to trigger '{1}' which does not contain an event or action applicable to ships; clearing trigger.", vesselType.Name, tokens[6]));
modified = true;
newShip.Trigger = Trigger.None;
}
}
@ -1294,26 +1362,32 @@ namespace MobiusEditor.RedAlert
if (techno is Building building)
{
errors.Add(string.Format("Ship '{0}' overlaps structure '{1}' in cell {2}; skipping.", vesselType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Ship '{0}' overlaps overlay '{1}' in cell {2}; skipping.", vesselType.Name, overlay.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Ship '{0}' overlaps terrain '{1}' in cell {2}; skipping.", vesselType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Ship '{0}' overlaps infantry in cell {1}; skipping.", vesselType.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Ship '{0}' overlaps unit '{1}' in cell {2}; skipping.", vesselType.Name, unit.Type.Name, cell));
modified = true;
}
else
{
errors.Add(string.Format("Ship '{0}' overlaps unknown techno in cell {1}; skipping.", vesselType.Name, cell));
modified = true;
}
}
}
@ -1322,10 +1396,12 @@ namespace MobiusEditor.RedAlert
if (tokens.Length < 2)
{
errors.Add(string.Format("Ship entry '{0}' has wrong number of tokens (expecting 7).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Ship '{0}' has wrong number of tokens (expecting 7).", tokens[1]));
modified = true;
}
}
}
@ -1342,23 +1418,27 @@ namespace MobiusEditor.RedAlert
if (infantryType == null)
{
errors.Add(string.Format("Infantry '{0}' references unknown infantry.", tokens[1]));
modified = true;
continue;
}
if (!aftermathEnabled && infantryType.IsExpansionUnit)
{
errors.Add(string.Format("Expansion infantry '{0}' encountered, but expansion units not enabled; enabling expansion units.", infantryType.Name));
modified = true;
Map.BasicSection.ExpansionEnabled = aftermathEnabled = true;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[3]));
modified = true;
continue;
}
var infantryGroup = Map.Technos[cell] as InfantryGroup;
@ -1373,6 +1453,7 @@ namespace MobiusEditor.RedAlert
if (!int.TryParse(tokens[4], out stoppingPos))
{
errors.Add(string.Format("Sub-position for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[4]));
modified = true;
continue;
}
if (stoppingPos < Globals.NumInfantryStops)
@ -1381,6 +1462,7 @@ namespace MobiusEditor.RedAlert
if (!int.TryParse(tokens[6], out dirValue))
{
errors.Add(string.Format("Direction for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[6]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1389,11 +1471,13 @@ namespace MobiusEditor.RedAlert
if (!checkTrigs.Contains(tokens[7]))
{
errors.Add(string.Format("Infantry '{0}' links to unknown trigger '{1}'; clearing trigger.", infantryType.Name, tokens[7]));
modified = true;
tokens[7] = Trigger.None;
}
else if (!checkUnitTrigs.Contains(tokens[7]))
{
errors.Add(string.Format("Infantry '{0}' links to trigger '{1}' which does not contain an event or action applicable to infantry; clearing trigger.", infantryType.Name, tokens[7]));
modified = true;
tokens[7] = Trigger.None;
}
infantryGroup.Infantry[stoppingPos] = new Infantry(infantryGroup)
@ -1409,11 +1493,13 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(string.Format("Infantry '{0}' overlaps another infantry at position {1} in cell {2}; skipping.", infantryType.Name, stoppingPos, cell));
modified = true;
}
}
else
{
errors.Add(string.Format("Infantry '{0}' has invalid position {1} in cell {2}; skipping.", infantryType.Name, stoppingPos, cell));
modified = true;
}
}
else
@ -1422,22 +1508,27 @@ namespace MobiusEditor.RedAlert
if (techno is Building building)
{
errors.Add(string.Format("Infantry '{0}' overlaps structure '{1}' in cell {2}; skipping.", infantryType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Infantry '{0}' overlaps overlay '{1}' in cell {2}; skipping.", infantryType.Name, overlay.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Infantry '{0}' overlaps terrain '{1}' in cell {2}; skipping.", infantryType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Infantry '{0}' overlaps unit '{1}' in cell {2}; skipping.", infantryType.Name, unit.Type.Name, cell));
modified = true;
}
else
{
errors.Add(string.Format("Infantry '{0}' overlaps unknown techno in cell {1}; skipping.", infantryType.Name, cell));
modified = true;
}
}
}
@ -1446,10 +1537,12 @@ namespace MobiusEditor.RedAlert
if (tokens.Length < 2)
{
errors.Add(string.Format("Infantry entry '{0}' has wrong number of tokens (expecting 8).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Infantry '{0}' has wrong number of tokens (expecting 8).", tokens[1]));
modified = true;
}
}
}
@ -1466,29 +1559,34 @@ namespace MobiusEditor.RedAlert
if (buildingType == null)
{
errors.Add(string.Format("Structure '{0}' references unknown structure.", tokens[1]));
modified = true;
continue;
}
if (Globals.FilterTheaterObjects && buildingType.Theaters != null && !buildingType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Structure '{0}' is not available in the set theater; skipping.", buildingType.Name));
modified = true;
continue;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for structure '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for structure '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[3]));
modified = true;
continue;
}
int dirValue;
if (!int.TryParse(tokens[4], out dirValue))
{
errors.Add(string.Format("Direction for structure '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[4]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1509,18 +1607,20 @@ namespace MobiusEditor.RedAlert
if (!checkTrigs.Contains(tokens[5]))
{
errors.Add(string.Format("Structure '{0}' links to unknown trigger '{1}'; clearing trigger.", buildingType.Name, tokens[5]));
modified = true;
newBld.Trigger = Trigger.None;
}
else if (!checkStrcTrigs.Contains(tokens[5]))
{
errors.Add(string.Format("Structure '{0}' links to trigger '{1}' which does not contain an event or action applicable to structures; clearing trigger.", buildingType.Name, tokens[5]));
modified = true;
newBld.Trigger = Trigger.None;
}
}
else
{
var techno = Map.FindBlockingObject(cell, buildingType, out int blockingCell);
string reportCell = blockingCell == -1 ? cell.ToString() : "<unknown>";
string reportCell = blockingCell == -1 ? "<unknown>" : cell.ToString();
if (techno is Building building)
{
bool onBib = false;
@ -1533,37 +1633,45 @@ namespace MobiusEditor.RedAlert
if (onBib)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps bib of structure '{2}' in cell {3}; skipping.", buildingType.Name, cell, building.Type.Name, reportCell));
modified = true;
}
else
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps structure '{2}' in cell {3}; skipping.", buildingType.Name, cell, building.Type.Name, reportCell));
modified = true;
}
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps overlay '{2}' in cell {3}; skipping.", buildingType.Name, cell, overlay.Type.Name, reportCell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps terrain '{2}' in cell {3}; skipping.", buildingType.Name, cell, terrain.Type.Name, reportCell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps infantry in cell {2}; skipping.", buildingType.Name, cell, reportCell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps unit '{2}' in cell {3}; skipping.", buildingType.Name, cell, unit.Type.Name, reportCell));
modified = true;
}
else
{
if (blockingCell != -1)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps unknown techno in cell {2}; skipping.", buildingType.Name, cell, blockingCell));
modified = true;
}
else
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps unknown techno; skipping.", buildingType.Name, cell));
modified = true;
}
}
}
@ -1573,10 +1681,12 @@ namespace MobiusEditor.RedAlert
if (tokens.Length < 2)
{
errors.Add(string.Format("Structure entry '{0}' has wrong number of tokens (expecting 6).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Structure '{0}' has wrong number of tokens (expecting 6).", tokens[1]));
modified = true;
}
}
}
@ -1599,17 +1709,20 @@ namespace MobiusEditor.RedAlert
if (buildingType == null)
{
errors.Add(string.Format("Base rebuild entry {0} references unknown structure '{1}'.", priority, tokens[0]));
modified = true;
continue;
}
if (Globals.FilterTheaterObjects && buildingType.Theaters != null && !buildingType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Base rebuild entry {0} references structure '{1}' which is not available in the set theater; skipping.", priority, buildingType.Name));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[1], out cell))
{
errors.Add(string.Format("Cell for base rebuild entry '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[1]));
modified = true;
continue;
}
Map.Metrics.GetLocation(cell, out Point location);
@ -1632,11 +1745,13 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(string.Format("Base rebuild entry {0} has wrong number of tokens (expecting 2).", priority));
modified = true;
}
}
else if (!Key.Equals("Count", StringComparison.CurrentCultureIgnoreCase))
{
errors.Add(string.Format("Invalid base rebuild priority '{0}' (expecting integer).", Key));
modified = true;
}
}
}
@ -1661,22 +1776,26 @@ namespace MobiusEditor.RedAlert
if (cell != -1)
{
errors.Add(string.Format("Waypoint {0} cell value {1} out of range (expecting between {2} and {3}).", waypoint, cell, 0, Map.Metrics.Length - 1));
modified = true;
}
}
}
else if (cell != -1)
{
errors.Add(string.Format("Waypoint {0} out of range (expecting between {1} and {2}).", waypoint, 0, Map.Waypoints.Length - 1));
modified = true;
}
}
else
{
errors.Add(string.Format("Waypoint {0} has invalid cell '{1}' (expecting integer).", waypoint, Value));
modified = true;
}
}
else
{
errors.Add(string.Format("Invalid waypoint '{0}' (expecting integer).", Key));
modified = true;
}
}
}
@ -1701,21 +1820,25 @@ namespace MobiusEditor.RedAlert
else
{
errors.Add(string.Format("Cell trigger {0} links to trigger '{1}' which does not contain a placeable event; skipping.", cell, Value));
modified = true;
}
}
else
{
errors.Add(string.Format("Cell trigger {0} links to unknown trigger '{1}'; skipping.", cell, Value));
modified = true;
}
}
else
{
errors.Add(string.Format("Cell trigger {0} outside map bounds; skipping.", cell));
modified = true;
}
}
else
{
errors.Add(string.Format("Invalid cell trigger '{0}' (expecting integer).", Key));
modified = true;
}
}
}
@ -1754,10 +1877,11 @@ namespace MobiusEditor.RedAlert
}
foreach (var teamType in teamTypes)
{
string trigName = indexToName(Map.Triggers, teamType.Trigger, Trigger.None);
string trigName = indexToName(triggers, teamType.Trigger, Trigger.None);
if (!checkUnitTrigs.Contains(trigName))
{
errors.Add(string.Format("Team '{0}' links to trigger '{1}' which does not contain an event or action applicable to units; clearing trigger.", teamType.Name, trigName));
modified = true;
teamType.Trigger = Trigger.None;
}
else
@ -1765,27 +1889,27 @@ namespace MobiusEditor.RedAlert
teamType.Trigger = trigName;
}
}
foreach (var trigger in Map.Triggers)
foreach (var trigger in triggers)
{
trigger.Event1.Team = indexToName(teamTypes, trigger.Event1.Team, TeamType.None);
trigger.Event2.Team = indexToName(teamTypes, trigger.Event2.Team, TeamType.None);
trigger.Action1.Team = indexToName(teamTypes, trigger.Action1.Team, TeamType.None);
trigger.Action1.Trigger = indexToName(Map.Triggers, trigger.Action1.Trigger, Trigger.None);
trigger.Action1.Trigger = indexToName(triggers, trigger.Action1.Trigger, Trigger.None);
trigger.Action2.Team = indexToName(teamTypes, trigger.Action2.Team, TeamType.None);
trigger.Action2.Trigger = indexToName(Map.Triggers, trigger.Action2.Trigger, Trigger.None);
trigger.Action2.Trigger = indexToName(triggers, trigger.Action2.Trigger, Trigger.None);
}
// Sort
var comparer = new ExplorerComparer();
Map.TeamTypes.Clear();
Map.TeamTypes.AddRange(teamTypes.OrderBy(t => t.Name, comparer));
UpdateBasePlayerHouse();
List<Trigger> reorderedTriggers = Map.Triggers.OrderBy(t => t.Name, comparer).ToList();
// Won't trigger the automatic cleanup and notifications.
Map.Triggers.Clear();
Map.Triggers.AddRange(reorderedTriggers);
Map.Triggers.AddRange(triggers.OrderBy(t => t.Name, comparer));
extraSections = ini.Sections;
bool switchedToSolo = forceSoloMission && !Map.BasicSection.SoloMission
&& (reorderedTriggers.Any(t => t.Action1.ActionType == ActionTypes.TACTION_WIN) || reorderedTriggers.Any(t => t.Action2.ActionType == ActionTypes.TACTION_WIN))
&& (reorderedTriggers.Any(t => t.Action1.ActionType == ActionTypes.TACTION_LOSE) || reorderedTriggers.Any(t => t.Action2.ActionType == ActionTypes.TACTION_LOSE));
&& (Map.Triggers.Any(t => t.Action1.ActionType == ActionTypes.TACTION_WIN) || Map.Triggers.Any(t => t.Action2.ActionType == ActionTypes.TACTION_WIN))
&& (Map.Triggers.Any(t => t.Action1.ActionType == ActionTypes.TACTION_LOSE) || Map.Triggers.Any(t => t.Action2.ActionType == ActionTypes.TACTION_LOSE));
if (switchedToSolo)
{
errors.Insert(0, "Filename detected as classic single player mission format, and win and lose trigger detected. Applying \"SoloMission\" flag.");
@ -1958,7 +2082,7 @@ namespace MobiusEditor.RedAlert
using (var jsonStream = new MemoryStream())
using (var iniWriter = new StreamWriter(iniStream))
using (var jsonWriter = new JsonTextWriter(new StreamWriter(jsonStream)))
using (var megafileBuilder = new MegafileBuilder(@"", path))
using (var megafileBuilder = new MegafileBuilder(String.Empty, path))
{
iniWriter.Write(ini.ToString());
iniWriter.Flush();
@ -2010,7 +2134,7 @@ namespace MobiusEditor.RedAlert
}
}
Model.BasicSection basic = Map.BasicSection;
// Make new section
// Make new Aftermath section
INISection newAftermathSection = new INISection("Aftermath");
newAftermathSection["NewUnitsEnabled"] = basic.ExpansionEnabled ? "1" : "0";
if (aftermathSection != null)
@ -2022,8 +2146,11 @@ namespace MobiusEditor.RedAlert
newAftermathSection[key] = value;
}
}
// Add Aftermath section
ini.Sections.Add(newAftermathSection);
// Add any other rules / unmanaged sections.
ini.Sections.AddRange(addedExtra);
// Clean up video names
char[] cutfrom = { ';', '(' };
basic.Intro = GeneralUtils.TrimRemarks(basic.Intro, true, cutfrom);
basic.Brief = GeneralUtils.TrimRemarks(basic.Brief, true, cutfrom);
@ -2035,7 +2162,7 @@ namespace MobiusEditor.RedAlert
basic.Lose = GeneralUtils.TrimRemarks(basic.Lose, true, cutfrom);
if (String.IsNullOrWhiteSpace(basic.Name))
{
string[] name = Path.GetFileNameWithoutExtension(fileName).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
string[] name = Path.GetFileNameWithoutExtension(fileName).Split(new[] { ' ', '_' }, StringSplitOptions.RemoveEmptyEntries);
for (Int32 i = 0; i < name.Length; i++)
{
String word = name[i];

View File

@ -435,8 +435,10 @@ namespace MobiusEditor.Render
int damageIcon = 0;
int collapseIcon = 0;
bool hasCollapseFrame = false;
// In TD, damage is when BELOW the threshold. In RA, it's ON the threshold.
int healthyMin = gameType == GameType.RedAlert ? 128 : 127;
// Only fetch if damaged. BuildingType.IsSingleFrame is an override for the RA mines. Everything else works with one simple logic.
if (building.Strength <= 128 && !building.Type.IsSingleFrame)
if (building.Strength <= healthyMin && !building.Type.IsSingleFrame)
{
maxIcon = Globals.TheTilesetManager.GetTileDataLength(theater.Tilesets, building.Type.Tilename);
hasCollapseFrame = (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor) && maxIcon > 1 && maxIcon % 2 == 1;
@ -446,7 +448,7 @@ namespace MobiusEditor.Render
if (building.Type.HasTurret)
{
icon = BodyShape[Facing32[building.Direction.ID]];
if (building.Strength <= 128)
if (building.Strength <= healthyMin)
{
icon += damageIcon;
}
@ -457,7 +459,7 @@ namespace MobiusEditor.Render
{
icon = collapseIcon;
}
else if (building.Strength <= 128)
else if (building.Strength <= healthyMin)
{
icon = damageIcon;
}
@ -474,7 +476,7 @@ namespace MobiusEditor.Render
if (building.Type.FactoryOverlay != null && (building.Strength > 1 || !hasCollapseFrame))
{
int overlayIcon = 0;
if (building.Strength <= 128)
if (building.Strength <= healthyMin)
{
int maxOverlayIcon = Globals.TheTilesetManager.GetTileDataLength(theater.Tilesets, building.Type.FactoryOverlay);
overlayIcon = maxOverlayIcon / 2;
@ -623,9 +625,10 @@ namespace MobiusEditor.Render
if (unit.Type == TiberianDawn.UnitTypes.GunBoat)
{
// East facing is not actually possible to set in missions. This is just the turret facing.
if (unit.Strength <= 128)
// In TD, damage is when BELOW the threshold. In RA, it's ON the threshold.
if (unit.Strength < 128)
icon += 32;
if (unit.Strength <= 64)
if (unit.Strength < 64)
icon += 32;
}
}
@ -807,6 +810,12 @@ namespace MobiusEditor.Render
}
public static (Rectangle, Action<Graphics>) Render(GameType gameType, bool soloMission, TheaterType theater, Size tileSize, TeamColor[] flagColors, Waypoint waypoint)
{
float defaultOpacity = !soloMission && (waypoint.Flag & WaypointFlag.PlayerStart) == WaypointFlag.PlayerStart ? 1.0f : 0.5f;
return Render(gameType, soloMission, theater, tileSize, flagColors, waypoint, defaultOpacity);
}
public static (Rectangle, Action<Graphics>) Render(GameType gameType, bool soloMission, TheaterType theater, Size tileSize, TeamColor[] flagColors, Waypoint waypoint, float transparencyModifier)
{
if (!waypoint.Point.HasValue)
{
@ -817,12 +826,11 @@ namespace MobiusEditor.Render
TeamColor teamColor = null;
Color tint = waypoint.Tint;
float brightness = 1.0f;
float transMod = 0.5f;
if (!soloMission && (waypoint.Flag & WaypointFlag.PlayerStart) == WaypointFlag.PlayerStart)
{
tileGraphics = "flagfly";
// Always paint flags as opaque.
transMod = 1.0f;
transparencyModifier = 1.0f;
int pls = (int)WaypointFlag.PlayerStart;
int flagId = ((int)waypoint.Flag & ~pls) / (pls << 1);
int mpId = 0;
@ -857,14 +865,14 @@ namespace MobiusEditor.Render
{
var imageAttributes = new ImageAttributes();
// Waypoints get drawn as semitransparent, so always execute this.
if (tint != Color.White || brightness != 1.0 || transMod != 1.0)
if (tint != Color.White || brightness != 1.0 || transparencyModifier != 1.0)
{
var colorMatrix = new ColorMatrix(new float[][]
{
new float[] {tint.R * brightness / 255.0f, 0, 0, 0, 0},
new float[] {0, tint.G * brightness / 255.0f, 0, 0, 0},
new float[] {0, 0, tint.B * brightness / 255.0f, 0, 0},
new float[] {0, 0, 0, (tint.A * transMod) / 255.0f, 0},
new float[] {0, 0, 0, (tint.A * transparencyModifier) / 255.0f, 0},
new float[] {0, 0, 0, 0, 1},
});
imageAttributes.SetColorMatrix(colorMatrix);
@ -1236,8 +1244,8 @@ namespace MobiusEditor.Render
};
var paintBounds = new Rectangle(new Point(topLeft.X * tileSize.Width, topLeft.Y * tileSize.Height), tileSize);
string wpText = waypoint.Name;
using (var baseBackgroundBrush = new SolidBrush(Color.FromArgb((forPreview ? 48 : 96) * 2 / 3, Color.Black)))
using (var baseTextBrush = new SolidBrush(Color.FromArgb(forPreview ? 64 : 128, textColor)))
using (var baseBackgroundBrush = new SolidBrush(Color.FromArgb(forPreview ? 64 : 128, Color.Black)))
using (var baseTextBrush = new SolidBrush(Color.FromArgb(forPreview ? 128 : 255, textColor)))
{
using (var font = graphics.GetAdjustedFont(wpText, SystemFonts.DefaultFont, paintBounds.Width,
Math.Max(1, (int)(12 * tileScale)), Math.Max(1, (int)(30 * tileScale)), true))

View File

@ -28,8 +28,9 @@ namespace MobiusEditor.SoleSurvivor
public static bool CheckForSSmap(string path, FileType fileType)
{
return CheckForMegamap(path, fileType) && GeneralUtils.CheckForIniInfo(path, fileType, "Crates", null, null);
return GeneralUtils.CheckForIniInfo(path, fileType, "Crates", null, null);
}
protected CratesSection cratesSection;
public CratesSection CratesSection => cratesSection;
@ -114,7 +115,7 @@ namespace MobiusEditor.SoleSurvivor
// Clean up this mess.
foreach (House house in Map.Houses)
{
if (house.Type.ID > HouseTypes.Multi1.ID)
if (house.Type.ID >= HouseTypes.Multi1.ID)
{
house.Enabled = false;
}
@ -130,9 +131,9 @@ namespace MobiusEditor.SoleSurvivor
return Load(path, fileType, true, out modified);
}
protected override List<string> LoadINI(INI ini, bool forceSoloMission)
protected override List<string> LoadINI(INI ini, bool forceSoloMission, ref bool modified)
{
List<string> errors = LoadINI(ini, forceSoloMission, true);
List<string> errors = LoadINI(ini, forceSoloMission, true, ref modified);
var cratesIniSection = extraSections.Extract("Crates");
if (cratesIniSection != null)
{
@ -181,7 +182,7 @@ namespace MobiusEditor.SoleSurvivor
SaveIniStructures(ini);
SaveINITerrain(ini);
SaveIniOverlay(ini);
if (waypointBackup != null)
if (overlayBackup != null)
{
ini.Sections.Add(overlayBackup);
}

View File

@ -55,8 +55,8 @@ namespace MobiusEditor.TiberianDawn
public static readonly BuildingType V09 = new BuildingType(31, "v09", "TEXT_STRUCTURE_TITLE_CIV9", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly BuildingType V10 = new BuildingType(32, "v10", "TEXT_STRUCTURE_TITLE_CIV10", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly BuildingType V11 = new BuildingType(33, "v11", "TEXT_STRUCTURE_TITLE_CIV11", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly BuildingType V12 = new BuildingType(34, "v12", "TEXT_STRUCTURE_TITLE_CIV12", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate });
public static readonly BuildingType V13 = new BuildingType(35, "v13", "TEXT_STRUCTURE_TITLE_CIV12", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate });
public static readonly BuildingType V12 = new BuildingType(34, "v12", "TEXT_STRUCTURE_TITLE_CIV12", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly BuildingType V13 = new BuildingType(35, "v13", "TEXT_STRUCTURE_TITLE_CIV12", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly BuildingType V14 = new BuildingType(36, "v14", "TEXT_STRUCTURE_TITLE_CIV13", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly BuildingType V15 = new BuildingType(37, "v15", "TEXT_STRUCTURE_TITLE_CIV14", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly BuildingType V16 = new BuildingType(38, "v16", "TEXT_STRUCTURE_TITLE_CIV15", 0, 0, new bool[1, 1] { { true } }, "Neutral", new [] { TheaterTypes.Temperate, TheaterTypes.Winter });

View File

@ -314,11 +314,12 @@ namespace MobiusEditor.TiberianDawn
}
ini.Parse(iniText);
forceSingle = !forSole && SinglePlayRegex.IsMatch(Path.GetFileNameWithoutExtension(path));
errors.AddRange(LoadINI(ini, forceSingle, forSole));
errors.AddRange(LoadINI(ini, forceSingle, ref modified));
}
if (!File.Exists(binPath))
{
errors.Add(String.Format("No .bin file found for file '{0}'. Using empty map.", Path.GetFileName(path)));
modified = true;
Map.Templates.Clear();
}
else
@ -328,11 +329,12 @@ namespace MobiusEditor.TiberianDawn
long mapLen = binReader.BaseStream.Length;
if ((!isMegaMap && mapLen == 0x2000) || (isMegaMap && mapLen % 4 == 0))
{
errors.AddRange(!isMegaMap ? LoadBinaryClassic(binReader) : LoadBinaryMega(binReader));
errors.AddRange(!isMegaMap ? LoadBinaryClassic(binReader, ref modified) : LoadBinaryMega(binReader, ref modified));
}
else
{
errors.Add(String.Format("'{0}' does not have the correct size for a " + this.Name + " .bin file.", Path.GetFileName(binPath)));
modified = true;
Map.Templates.Clear();
}
}
@ -357,15 +359,16 @@ namespace MobiusEditor.TiberianDawn
iniText = FixRoad2Load(iniText);
}
ini.Parse(iniText);
errors.AddRange(LoadINI(ini, false, forSole));
errors.AddRange(LoadINI(ini, false, ref modified));
long mapLen = binReader.BaseStream.Length;
if ((!isMegaMap && mapLen == 0x2000) || (isMegaMap && mapLen % 4 == 0))
{
errors.AddRange(!isMegaMap ? LoadBinaryClassic(binReader) : LoadBinaryMega(binReader));
errors.AddRange(!isMegaMap ? LoadBinaryClassic(binReader, ref modified) : LoadBinaryMega(binReader, ref modified));
}
else
{
errors.Add(String.Format("'{0}' does not have the correct size for a " + this.Name + " .bin file.", Path.GetFileName(binFile)));
modified = true;
Map.Templates.Clear();
}
}
@ -374,11 +377,6 @@ namespace MobiusEditor.TiberianDawn
default:
throw new NotSupportedException("Unsupported filetype.");
}
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;
}
@ -498,12 +496,12 @@ namespace MobiusEditor.TiberianDawn
return sb.ToString();
}
protected virtual List<string> LoadINI(INI ini, bool forceSoloMission)
protected virtual List<string> LoadINI(INI ini, bool forceSoloMission, ref bool modified)
{
return LoadINI(ini, forceSoloMission, false);
return LoadINI(ini, forceSoloMission, false, ref modified);
}
protected List<string> LoadINI(INI ini, bool forceSoloMission, bool forSole)
protected List<string> LoadINI(INI ini, bool forceSoloMission, bool forSole, ref bool modified)
{
var errors = new List<string>();
Map.BeginUpdate();
@ -584,8 +582,12 @@ namespace MobiusEditor.TiberianDawn
{
try
{
if (Key.Length > 8)
{
errors.Add(string.Format("TeamType '{0}' has a name that is longer than 8 characters. This will not be corrected by the loading process, but should be addressed, since it can make the teams fail to read correctly, and might even crash the game.", Key));
modified = true;
}
var teamType = new TeamType { Name = Key };
var tokens = Value.Split(',').ToList();
teamType.House = Map.HouseTypes.Where(t => t.Equals(tokens[0])).FirstOrDefault(); tokens.RemoveAt(0);
teamType.IsRoundAbout = int.Parse(tokens[0]) != 0; tokens.RemoveAt(0);
@ -614,11 +616,13 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Team '{0}' references unknown class '{1}'.", Key, classTokens[0]));
modified = true;
}
}
else
{
errors.Add(string.Format("Team '{0}' has wrong number of tokens for class index {1} (expecting 2).", Key, i));
modified = true;
}
}
var numMissions = int.Parse(tokens[0]); tokens.RemoveAt(0);
@ -639,11 +643,13 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Team '{0}' references unknown class '{1}'.", Key, missionTokens[0]));
modified = true;
}
}
else
{
errors.Add(string.Format("Team '{0}' has wrong number of tokens for mission index {1} (expecting 2).", Key, i));
modified = true;
}
}
if (tokens.Count > 0)
@ -659,6 +665,7 @@ namespace MobiusEditor.TiberianDawn
catch (Exception ex)
{
errors.Add(string.Format("Teamtype '{0}' has errors and can't be parsed: {1}.", Key, ex.Message));
modified = true;
}
}
}
@ -669,6 +676,11 @@ namespace MobiusEditor.TiberianDawn
{
try
{
if (Key.Length > 4)
{
errors.Add(string.Format("Trigger '{0}' has a name that is longer than 4 characters. This will not be corrected by the loading process, but should be addressed, since it can make the triggers fail to read correctly and link to objects and cell triggers, and might even crash the game.", Key));
modified = true;
}
var tokens = Value.Split(',');
if (tokens.Length >= 5)
{
@ -691,11 +703,13 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Trigger '{0}' has too few tokens (expecting at least 5).", Key));
modified = true;
}
}
catch (Exception ex)
{
errors.Add(string.Format("Trigger '{0}' has errors and can't be parsed: {1}.", Key, ex.Message));
modified = true;
}
}
}
@ -713,6 +727,7 @@ namespace MobiusEditor.TiberianDawn
if (!int.TryParse(Key, out cell))
{
errors.Add(string.Format("Cell for terrain cannot be parsed. Key: '{0}', value: '{1}'; skipping.", Key, Value));
modified = true;
continue;
}
var tokens = Value.Split(',');
@ -724,6 +739,7 @@ namespace MobiusEditor.TiberianDawn
if (Globals.FilterTheaterObjects && terrainType.Theaters != null && !terrainType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Terrain '{0}' is not available in the set theater; skipping.", terrainType.Name));
modified = true;
continue;
}
Terrain newTerr = new Terrain
@ -736,47 +752,57 @@ namespace MobiusEditor.TiberianDawn
if (!checkTrigs.Contains(newTerr.Trigger))
{
errors.Add(string.Format("Terrain '{0}' links to unknown trigger '{1}'; clearing trigger.", terrainType.Name, tokens[1]));
modified = true;
newTerr.Trigger = Trigger.None;
}
else if (!checkTerrTrigs.Contains(newTerr.Trigger))
{
errors.Add(string.Format("Terrain '{0}' links to trigger '{1}' which does not contain an event applicable to terrain; clearing trigger.", terrainType.Name, tokens[1]));
modified = true;
newTerr.Trigger = Trigger.None;
}
}
else
{
var techno = Map.FindBlockingObject(cell, terrainType, out int blockingCell);
string reportCell = blockingCell == -1 ? cell.ToString() : "<unknown>";
string reportCell = blockingCell == -1 ? "<unknown>" : cell.ToString();
if (techno is Building building)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps structure '{2}' in cell {3}; skipping.", terrainType.Name, cell, building.Type.Name, reportCell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps overlay '{2}' in cell {3}; skipping.", terrainType.Name, cell, overlay.Type.Name, reportCell));
modified = true;
}
else if (techno is Terrain terrain)
{
MessageBox.Show("overlap");
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps terrain '{2}' in cell {3}; skipping.", terrainType.Name, cell, terrain.Type.Name, reportCell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps infantry in cell {2}; skipping.", terrainType.Name, cell, reportCell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Terrain '{0}' on cell {1} overlaps unit '{2}' in cell {3}; skipping.", terrainType.Name, cell, unit.Type.Name, reportCell));
modified = true;
}
else
{
if (blockingCell != -1)
{
errors.Add(string.Format("Terrain '{0}' placed on cell {1} overlaps unknown techno in cell {2}; skipping.", terrainType.Name, cell, reportCell));
modified = true;
}
else
{
errors.Add(string.Format("Terrain '{0}' placed on cell {1} overlaps unknown techno; skipping.", terrainType.Name, cell));
modified = true;
}
}
}
@ -784,11 +810,13 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Terrain '{0}' references unknown terrain.", tokens[0]));
modified = true;
}
}
else
{
errors.Add(string.Format("Terrain '{0}' has wrong number of tokens (expecting 2).", Key));
modified = true;
}
}
}
@ -801,6 +829,7 @@ namespace MobiusEditor.TiberianDawn
if (!int.TryParse(Key, out cell))
{
errors.Add(string.Format("Cell for overlay cannot be parsed. Key: '{0}', value: '{1}'; skipping.", Key, Value));
modified = true;
continue;
}
var overlayType = Map.OverlayTypes.Where(t => t.Equals(Value)).FirstOrDefault();
@ -809,6 +838,7 @@ namespace MobiusEditor.TiberianDawn
if (Globals.FilterTheaterObjects && overlayType.Theaters != null && !overlayType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Overlay '{0}' is not available in the set theater; skipping.", overlayType.Name));
modified = true;
continue;
}
Map.Overlay[cell] = new Overlay { Type = overlayType, Icon = 0 };
@ -816,6 +846,7 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Overlay '{0}' references unknown overlay.", Value));
modified = true;
}
}
}
@ -828,6 +859,7 @@ namespace MobiusEditor.TiberianDawn
if (!int.TryParse(Key, out cell))
{
errors.Add(string.Format("Cell for Smudge cannot be parsed. Key: '{0}', value: '{1}'; skipping.", Key, Value));
modified = true;
continue;
}
var tokens = Value.Split(',');
@ -841,11 +873,13 @@ namespace MobiusEditor.TiberianDawn
if (Globals.FilterTheaterObjects && smudgeType.Theaters != null && !smudgeType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Smudge '{0}' is not available in the set theater; skipping.", smudgeType.Name));
modified = true;
continue;
}
if (badCrater)
{
errors.Add(string.Format("Smudge '{0}' does not function correctly in maps. Correcting to '{1}'.", tokens[0], smudgeType.Name));
modified = true;
}
int icon = 0;
if (smudgeType.Icons > 1 && int.TryParse(tokens[2], out icon))
@ -874,11 +908,13 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Smudge '{0}' references unknown smudge.", tokens[0]));
modified = true;
}
}
else
{
errors.Add(string.Format("Smudge on cell '{0}' has wrong number of tokens (expecting 3).", Key));
modified = true;
}
}
}
@ -894,18 +930,21 @@ namespace MobiusEditor.TiberianDawn
if (infantryType == null)
{
errors.Add(string.Format("Infantry '{0}' references unknown infantry.", tokens[1]));
modified = true;
continue;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[3]));
modified = true;
continue;
}
var infantryGroup = Map.Technos[cell] as InfantryGroup;
@ -920,6 +959,7 @@ namespace MobiusEditor.TiberianDawn
if (!int.TryParse(tokens[4], out stoppingPos))
{
errors.Add(string.Format("Sub-position for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[4]));
modified = true;
continue;
}
if (stoppingPos < Globals.NumInfantryStops)
@ -928,6 +968,7 @@ namespace MobiusEditor.TiberianDawn
if (!int.TryParse(tokens[6], out dirValue))
{
errors.Add(string.Format("Direction for infantry '{0}' cannot be parsed; value: '{1}'; skipping.", infantryType.Name, tokens[6]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -936,11 +977,13 @@ namespace MobiusEditor.TiberianDawn
if (!checkTrigs.Contains(tokens[7]))
{
errors.Add(string.Format("Infantry '{0}' links to unknown trigger '{1}'; clearing trigger.", infantryType.Name, tokens[7]));
modified = true;
tokens[7] = Trigger.None;
}
else if (!checkUnitTrigs.Contains(tokens[7]))
{
errors.Add(string.Format("Infantry '{0}' links to trigger '{1}' which does not contain an event applicable to infantry; clearing trigger.", infantryType.Name, tokens[7]));
modified = true;
tokens[7] = Trigger.None;
}
infantryGroup.Infantry[stoppingPos] = new Infantry(infantryGroup)
@ -956,11 +999,13 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Infantry '{0}' overlaps another infantry at position {1} in cell {2}; skipping.", infantryType.Name, stoppingPos, cell));
modified = true;
}
}
else
{
errors.Add(string.Format("Infantry '{0}' has invalid position {1} in cell {2}; skipping.", infantryType.Name, stoppingPos, cell));
modified = true;
}
}
else
@ -969,22 +1014,27 @@ namespace MobiusEditor.TiberianDawn
if (techno is Building building)
{
errors.Add(string.Format("Infantry '{0}' overlaps structure '{1}' in cell {2}; skipping.", infantryType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Infantry '{0}' overlaps overlay '{1}' in cell {2}; skipping.", infantryType.Name, overlay.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Infantry '{0}' overlaps terrain '{1}' in cell {2}; skipping.", infantryType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Infantry '{0}' overlaps unit '{1}' in cell {2}; skipping.", infantryType.Name, unit.Type.Name, cell));
modified = true;
}
else
{
errors.Add(string.Format("Infantry '{0}' overlaps unknown techno in cell {1}; skipping.", infantryType.Name, cell));
modified = true;
}
}
}
@ -993,10 +1043,12 @@ namespace MobiusEditor.TiberianDawn
if (tokens.Length < 2)
{
errors.Add(string.Format("Infantry entry '{0}' has wrong number of tokens (expecting 8).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Infantry '{0}' has wrong number of tokens (expecting 8).", tokens[1]));
modified = true;
}
}
}
@ -1013,24 +1065,28 @@ namespace MobiusEditor.TiberianDawn
if (unitType == null)
{
errors.Add(string.Format("Unit '{0}' references unknown unit.", tokens[1]));
modified = true;
continue;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for unit '{0}' cannot be parsed; value: '{1}'; skipping.", unitType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for unit '{0}' cannot be parsed; value: '{1}'; skipping.", unitType.Name, tokens[3]));
modified = true;
continue;
}
int dirValue;
if (!int.TryParse(tokens[4], out dirValue))
{
errors.Add(string.Format("Direction for unit '{0}' cannot be parsed; value: '{1}'; skipping.", unitType.Name, tokens[4]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1053,11 +1109,13 @@ namespace MobiusEditor.TiberianDawn
if (!checkTrigs.Contains(tokens[6]))
{
errors.Add(string.Format("Unit '{0}' links to unknown trigger '{1}'; clearing trigger.", unitType.Name, newUnit.Trigger));
modified = true;
newUnit.Trigger = Trigger.None;
}
else if (!checkUnitTrigs.Contains(tokens[6]))
{
errors.Add(string.Format("Unit '{0}' links to trigger '{1}' which does not contain an event applicable to units; clearing trigger.", unitType.Name, newUnit.Trigger));
modified = true;
newUnit.Trigger = Trigger.None;
}
}
@ -1067,26 +1125,32 @@ namespace MobiusEditor.TiberianDawn
if (techno is Building building)
{
errors.Add(string.Format("Unit '{0}' overlaps structure '{1}' in cell {2}; skipping.", unitType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Unit '{0}' overlaps overlay '{1}' in cell {2}; skipping.", unitType.Name, overlay.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Unit '{0}' overlaps terrain '{1}' in cell {2}; skipping.", unitType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Unit '{0}' overlaps infantry in cell {1}; skipping.", unitType.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Unit '{0}' overlaps unit '{1}' in cell {2}; skipping.", unitType.Name, unit.Type.Name, cell));
modified = true;
}
else
{
errors.Add(string.Format("Unit '{0}' overlaps unknown techno in cell {1}; skipping.", unitType.Name, cell));
modified = true;
}
}
}
@ -1095,10 +1159,12 @@ namespace MobiusEditor.TiberianDawn
if (tokens.Length < 2)
{
errors.Add(string.Format("Unit entry '{0}' has wrong number of tokens (expecting 7).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Unit '{0}' has wrong number of tokens (expecting 7).", tokens[1]));
modified = true;
}
}
}
@ -1117,24 +1183,28 @@ namespace MobiusEditor.TiberianDawn
if (aircraftType == null)
{
errors.Add(string.Format("Aircraft '{0}' references unknown aircraft.", tokens[1]));
modified = true;
continue;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", aircraftType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", aircraftType.Name, tokens[3]));
modified = true;
continue;
}
int dirValue;
if (!int.TryParse(tokens[4], out dirValue))
{
errors.Add(string.Format("Direction for aircraft '{0}' cannot be parsed; value: '{1}'; skipping.", aircraftType.Name, tokens[4]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1152,26 +1222,32 @@ namespace MobiusEditor.TiberianDawn
if (techno is Building building)
{
errors.Add(string.Format("Aircraft '{0}' overlaps structure '{1}' in cell {2}; skipping.", aircraftType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Aircraft '{0}' overlaps overlay '{1}' in cell {2}; skipping.", aircraftType.Name, overlay.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Aircraft '{0}' overlaps terrain '{1}' in cell {2}; skipping.", aircraftType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Aircraft '{0}' overlaps infantry in cell {1}; skipping.", aircraftType.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Aircraft '{0}' overlaps unit '{1}' in cell {2}; skipping.", aircraftType.Name, unit.Type.Name, cell));
modified = true;
}
else
{
errors.Add(string.Format("Aircraft '{0}' overlaps unknown techno in cell {1}; skipping.", aircraftType.Name, cell));
modified = true;
}
}
}
@ -1180,10 +1256,12 @@ namespace MobiusEditor.TiberianDawn
if (tokens.Length < 2)
{
errors.Add(string.Format("Aircraft entry '{0}' has wrong number of tokens (expecting 6).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Aircraft '{0}' has wrong number of tokens (expecting 6).", tokens[1]));
modified = true;
}
}
}
@ -1200,29 +1278,34 @@ namespace MobiusEditor.TiberianDawn
if (buildingType == null)
{
errors.Add(string.Format("Structure '{0}' references unknown structure.", tokens[1]));
modified = true;
continue;
}
if (Globals.FilterTheaterObjects && buildingType.Theaters != null && !buildingType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Structure '{0}' is not available in the set theater; skipping.", buildingType.Name));
modified = true;
continue;
}
int strength;
if (!int.TryParse(tokens[2], out strength))
{
errors.Add(string.Format("Strength for structure '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[2]));
modified = true;
continue;
}
int cell;
if (!int.TryParse(tokens[3], out cell))
{
errors.Add(string.Format("Cell for structure '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[3]));
modified = true;
continue;
}
int dirValue;
if (!int.TryParse(tokens[4], out dirValue))
{
errors.Add(string.Format("Direction for structure '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[4]));
modified = true;
continue;
}
var direction = (byte)((dirValue + 0x08) & ~0x0F);
@ -1239,18 +1322,20 @@ namespace MobiusEditor.TiberianDawn
if (!checkTrigs.Contains(tokens[5]))
{
errors.Add(string.Format("Structure '{0}' links to unknown trigger '{1}'; clearing trigger.", buildingType.Name, tokens[5]));
modified = true;
newBld.Trigger = Trigger.None;
}
else if (!checkStrcTrigs.Contains(tokens[5]))
{
errors.Add(string.Format("Structure '{0}' links to trigger '{1}' which does not contain an event applicable to structures; clearing trigger.", buildingType.Name, tokens[5]));
modified = true;
newBld.Trigger = Trigger.None;
}
}
else
{
var techno = Map.FindBlockingObject(cell, buildingType, out int blockingCell);
string reportCell = blockingCell == -1 ? cell.ToString() : "<unknown>";
string reportCell = blockingCell == -1 ? "<unknown>" : cell.ToString();
if (techno is Building building)
{
bool onBib = false;
@ -1263,37 +1348,45 @@ namespace MobiusEditor.TiberianDawn
if (onBib)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps bib of structure '{2}' in cell {3}; skipping.", buildingType.Name, cell, building.Type.Name, reportCell));
modified = true;
}
else
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps structure '{2}' in cell {3}; skipping.", buildingType.Name, cell, building.Type.Name, reportCell));
modified = true;
}
}
else if (techno is Overlay overlay)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps overlay '{2}' in cell {3}; skipping.", buildingType.Name, cell, overlay.Type.Name, reportCell));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps terrain '{2}' in cell {3}; skipping.", buildingType.Name, cell, terrain.Type.Name, reportCell));
modified = true;
}
else if (techno is InfantryGroup infantry)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps infantry in cell {2}; skipping.", buildingType.Name, cell, reportCell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps unit '{2}' in cell {3}; skipping.", buildingType.Name, cell, unit.Type.Name, reportCell));
modified = true;
}
else
{
if (blockingCell != -1)
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps unknown techno in cell {2}; skipping.", buildingType.Name, cell, blockingCell));
modified = true;
}
else
{
errors.Add(string.Format("Structure '{0}' placed on cell {1} overlaps unknown techno; skipping.", buildingType.Name, cell));
modified = true;
}
}
}
@ -1303,10 +1396,12 @@ namespace MobiusEditor.TiberianDawn
if (tokens.Length < 2)
{
errors.Add(string.Format("Structure entry '{0}' has wrong number of tokens (expecting 6).", Key));
modified = true;
}
else
{
errors.Add(string.Format("Structure '{0}' has wrong number of tokens (expecting 6).", tokens[1]));
modified = true;
}
}
}
@ -1327,12 +1422,14 @@ namespace MobiusEditor.TiberianDawn
if (Globals.FilterTheaterObjects && buildingType.Theaters != null && !buildingType.Theaters.Contains(Map.Theater))
{
errors.Add(string.Format("Base rebuild entry {0} references structure '{1}' which is not available in the set theater; skipping.", priority, buildingType.Name));
modified = true;
continue;
}
int coord;
if (!int.TryParse(tokens[1], out coord))
{
errors.Add(string.Format("Coordinates for base rebuild entry '{0}' cannot be parsed; value: '{1}'; skipping.", buildingType.Name, tokens[1]));
modified = true;
continue;
}
// Preparations for megamap support.
@ -1357,16 +1454,19 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Base rebuild entry {0} references unknown structure '{1}'.", priority, tokens[0]));
modified = true;
}
}
else
{
errors.Add(string.Format("Base rebuild entry {0} has wrong number of tokens (expecting 2).", priority));
modified = true;
}
}
else if (!Key.Equals("Count", StringComparison.CurrentCultureIgnoreCase))
{
errors.Add(string.Format("Invalid base rebuild priority '{0}' (expecting integer).", Key));
modified = true;
}
}
}
@ -1391,22 +1491,26 @@ namespace MobiusEditor.TiberianDawn
if (cell != -1)
{
errors.Add(string.Format("Waypoint {0} cell value {1} out of range (expecting between {2} and {3}).", waypoint, cell, 0, Map.Metrics.Length - 1));
modified = true;
}
}
}
else if (cell != -1)
{
errors.Add(string.Format("Waypoint {0} out of range (expecting between {1} and {2}).", waypoint, 0, Map.Waypoints.Length - 1));
modified = true;
}
}
else
{
errors.Add(string.Format("Waypoint {0} has invalid cell '{1}' (expecting integer).", waypoint, Value));
modified = true;
}
}
else
{
errors.Add(string.Format("Invalid waypoint '{0}' (expecting integer).", Key));
modified = true;
}
}
}
@ -1431,21 +1535,25 @@ namespace MobiusEditor.TiberianDawn
else
{
errors.Add(string.Format("Cell trigger {0} links to trigger '{1}' which does not contain a placeable event; skipping.", cell, Value));
modified = true;
}
}
else
{
errors.Add(string.Format("Cell trigger {0} links to unknown trigger '{1}'; skipping.", cell, Value));
modified = true;
}
}
else
{
errors.Add(string.Format("Cell trigger {0} is outside map bounds; skipping.", cell));
modified = true;
}
}
else
{
errors.Add(string.Format("Invalid cell trigger '{0}' (expecting integer).", Key));
modified = true;
}
}
}
@ -1484,8 +1592,8 @@ namespace MobiusEditor.TiberianDawn
Map.TeamTypes.Sort((x, y) => comparer.Compare(x.Name, y.Name));
extraSections = ini.Sections;
bool switchedToSolo = !forSole && forceSoloMission && !Map.BasicSection.SoloMission
&& reorderedTriggers.Any(t => t.Action1.ActionType == ActionTypes.ACTION_WIN)
&& reorderedTriggers.Any(t => t.Action1.ActionType == ActionTypes.ACTION_LOSE);
&& ((reorderedTriggers.Any(t => t.Action1.ActionType == ActionTypes.ACTION_WIN) && reorderedTriggers.Any(t => t.Action1.ActionType == ActionTypes.ACTION_LOSE))
|| reorderedTriggers.Any(t => t.Event1.EventType == EventTypes.EVENT_ANY && t.Action1.ActionType == ActionTypes.ACTION_WINLOSE));
if (switchedToSolo)
{
errors.Insert(0, "Filename detected as classic single player mission format, and win and lose trigger detected. Applying \"SoloMission\" flag.");
@ -1495,7 +1603,7 @@ namespace MobiusEditor.TiberianDawn
return errors;
}
protected IEnumerable<string> LoadBinaryClassic(BinaryReader reader)
protected IEnumerable<string> LoadBinaryClassic(BinaryReader reader, ref bool modified)
{
var errors = new List<string>();
Map.Templates.Clear();
@ -1505,28 +1613,38 @@ namespace MobiusEditor.TiberianDawn
{
var typeValue = reader.ReadByte();
var iconValue = reader.ReadByte();
TemplateType templateType = ChecKTemplateType(typeValue, iconValue, x, y, errors);
TemplateType templateType = ChecKTemplateType(typeValue, iconValue, x, y, errors, ref modified);
Map.Templates[y, x] = (templateType != null) ? new Template { Type = templateType, Icon = iconValue } : null;
}
}
return errors;
}
protected IEnumerable<string> LoadBinaryMega(BinaryReader reader)
protected IEnumerable<string> LoadBinaryMega(BinaryReader reader, ref bool modified)
{
var errors = new List<string>();
Map.Templates.Clear();
long dataLen = reader.BaseStream.Length;
int mapLen = Map.Metrics.Length;
int mapWidth = Map.Metrics.Width;
int lastCell = -1;
while (reader.BaseStream.Position < dataLen)
{
byte cellLow = reader.ReadByte();
byte cellHi = reader.ReadByte();
int cell = (cellHi << 8) | cellLow;
if (cell == lastCell)
{
errors.Add(String.Format("Map contains duplicate cell numbers.", cell));
}
else if (cell < lastCell)
{
errors.Add(String.Format("Map cell numbers are not in sequential order.", cell));
}
if (cell > mapLen)
{
errors.Add(String.Format("Map contains cell number '{0}' which is too large for a TD MegaMap.", cell));
modified = true;
// Just abort I guess?
break;
}
@ -1534,13 +1652,13 @@ namespace MobiusEditor.TiberianDawn
int x = cell % mapWidth;
byte typeValue = reader.ReadByte();
byte iconValue = reader.ReadByte();
TemplateType templateType = ChecKTemplateType(typeValue, iconValue, x, y, errors);
TemplateType templateType = ChecKTemplateType(typeValue, iconValue, x, y, errors, ref modified);
Map.Templates[y,x] = (templateType != null) ? new Template { Type = templateType, Icon = iconValue } : null;
}
return errors;
}
protected TemplateType ChecKTemplateType(int typeValue, int iconValue, int x, int y, List<string> errors)
protected TemplateType ChecKTemplateType(int typeValue, int iconValue, int x, int y, List<string> errors, ref bool modified)
{
TemplateType templateType = Map.TemplateTypes.Where(t => t.Equals(typeValue)).FirstOrDefault();
// Prevent loading of illegal tiles.
@ -1555,22 +1673,26 @@ namespace MobiusEditor.TiberianDawn
else if (templateType.Theaters != null && !templateType.Theaters.Contains(Map.Theater))
{
errors.Add(String.Format("Template '{0}' at cell [{1},{2}] is not available in the set theater; clearing.", templateType.Name.ToUpper(), x, y));
modified = true;
templateType = null;
}
else if (iconValue >= templateType.NumIcons)
{
errors.Add(String.Format("Template '{0}' at cell [{1},{2}] has an icon set ({3}) that is outside its icons range; clearing.", templateType.Name.ToUpper(), x, y, iconValue));
modified = true;
templateType = null;
}
else if (!isRandom && templateType.IconMask != null && !templateType.IconMask[iconValue / templateType.IconWidth, iconValue % templateType.IconWidth])
{
errors.Add(String.Format("Template '{0}' at cell [{1},{2}] has an icon set ({3}) that is not part of its placeable cells; clearing.", templateType.Name.ToUpper(), x, y, iconValue));
modified = true;
templateType = null;
}
}
else if (typeValue != 0xFF)
{
errors.Add(String.Format("Unknown template value {0:X2} at cell [{1},{2}]; clearing.", typeValue, x, y));
modified = true;
}
return templateType;
}
@ -1585,9 +1707,9 @@ namespace MobiusEditor.TiberianDawn
return Save(path, fileType, false, customPreview);
}
public bool Save(string path, FileType fileType, bool forSS, Bitmap customPreview)
public bool Save(string path, FileType fileType, bool forSole, Bitmap customPreview)
{
String errors = Validate(forSS);
String errors = Validate(forSole);
if (errors != null)
{
MessageBox.Show(errors, "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
@ -1603,7 +1725,7 @@ namespace MobiusEditor.TiberianDawn
SaveINI(ini, fileType, path);
using (var iniWriter = new StreamWriter(iniPath))
{
if (forSS)
if (forSole)
{
iniWriter.Write(ini.ToString());
}
@ -1624,8 +1746,8 @@ namespace MobiusEditor.TiberianDawn
SaveBinaryMega(binWriter);
}
}
// None of this junk for SS.
if (!forSS && (!Map.BasicSection.SoloMission || !Properties.Settings.Default.NoMetaFilesForSinglePlay))
// None of this junk for Sole Survivor.
if (!forSole && (!Map.BasicSection.SoloMission || !Properties.Settings.Default.NoMetaFilesForSinglePlay))
{
var tgaPath = Path.ChangeExtension(path, ".tga");
var jsonPath = Path.ChangeExtension(path, ".json");
@ -1657,9 +1779,9 @@ namespace MobiusEditor.TiberianDawn
using (var iniWriter = new StreamWriter(iniStream))
using (var binWriter = new BinaryWriter(binStream))
using (var jsonWriter = new JsonTextWriter(new StreamWriter(jsonStream)))
using (var megafileBuilder = new MegafileBuilder(@"", path))
using (var megafileBuilder = new MegafileBuilder(String.Empty, path))
{
if (forSS)
if (forSole)
{
iniWriter.Write(ini.ToString());
}
@ -1790,7 +1912,7 @@ namespace MobiusEditor.TiberianDawn
basic.Lose = GeneralUtils.TrimRemarks(basic.Lose, true, cutfrom);
if (String.IsNullOrWhiteSpace(basic.Name))
{
string[] name = Path.GetFileNameWithoutExtension(fileName).Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
string[] name = Path.GetFileNameWithoutExtension(fileName).Split(new[] { ' ', '_' }, StringSplitOptions.RemoveEmptyEntries);
for (Int32 i = 0; i < name.Length; i++)
{
String word = name[i];

View File

@ -46,8 +46,8 @@ namespace MobiusEditor.TiberianDawn
public static readonly OverlayType Road2 = new OverlayType(19, "roadfullslab", "Concrete Road (full)", null, OverlayTypeFlag.Decoration, "road", 1);
// Not available to place down sadly: even the ini read for it in the game code only succeeds if 'IsGross' is enabled.
//public static readonly OverlayType Squishy = new OverlayType(19, "SQUISH", OverlayTypeFlag.Decoration);
public static readonly OverlayType V12 = new OverlayType(20, "v12", "TEXT_STRUCTURE_TITLE_CIV12", new[] { TheaterTypes.Temperate });
public static readonly OverlayType V13 = new OverlayType(21, "v13", "TEXT_STRUCTURE_TITLE_CIV12", new[] { TheaterTypes.Temperate });
public static readonly OverlayType V12 = new OverlayType(20, "v12", "TEXT_STRUCTURE_TITLE_CIV12", new[] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly OverlayType V13 = new OverlayType(21, "v13", "TEXT_STRUCTURE_TITLE_CIV12", new[] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly OverlayType V14 = new OverlayType(22, "v14", "TEXT_STRUCTURE_TITLE_CIV13", new[] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly OverlayType V15 = new OverlayType(23, "v15", "TEXT_STRUCTURE_TITLE_CIV14", new[] { TheaterTypes.Temperate, TheaterTypes.Winter });
public static readonly OverlayType V16 = new OverlayType(24, "v16", "TEXT_STRUCTURE_TITLE_CIV15", new[] { TheaterTypes.Temperate, TheaterTypes.Winter });

View File

@ -26,7 +26,8 @@ namespace MobiusEditor.TiberianDawn
public static readonly TheaterType Desert = new TheaterType(0, "desert", "TD_Terrain_Desert".Yield().Concat(commonTilesets));
public static readonly TheaterType Temperate = new TheaterType(2, "temperate", "TD_Terrain_Temperate".Yield().Concat(commonTilesets));
public static readonly TheaterType Winter = new TheaterType(2, "winter", "TD_Terrain_Winter".Yield().Concat(commonTilesets));
// Winter seems to fall back on Temperate for the Haystack graphics.
public static readonly TheaterType Winter = new TheaterType(2, "winter", "TD_Terrain_Winter".Yield().Concat(commonTilesets).Concat("TD_Terrain_Temperate".Yield()));
private static TheaterType[] Types;

View File

@ -65,7 +65,7 @@ namespace MobiusEditor.Tools.Dialogs
this.tableLayoutPanel4.RowCount = 2;
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel4.Size = new System.Drawing.Size(145, 77);
this.tableLayoutPanel4.Size = new System.Drawing.Size(200, 77);
this.tableLayoutPanel4.TabIndex = 3;
//
// lblWaypoint
@ -86,7 +86,7 @@ namespace MobiusEditor.Tools.Dialogs
this.waypointCombo.FormattingEnabled = true;
this.waypointCombo.Location = new System.Drawing.Point(61, 3);
this.waypointCombo.Name = "waypointCombo";
this.waypointCombo.Size = new System.Drawing.Size(81, 21);
this.waypointCombo.Size = new System.Drawing.Size(136, 21);
this.waypointCombo.TabIndex = 1;
//
// btnJumpTo
@ -94,7 +94,7 @@ namespace MobiusEditor.Tools.Dialogs
this.btnJumpTo.Dock = System.Windows.Forms.DockStyle.Top;
this.btnJumpTo.Location = new System.Drawing.Point(61, 30);
this.btnJumpTo.Name = "btnJumpTo";
this.btnJumpTo.Size = new System.Drawing.Size(81, 23);
this.btnJumpTo.Size = new System.Drawing.Size(136, 23);
this.btnJumpTo.TabIndex = 2;
this.btnJumpTo.Text = "Jump to...";
this.btnJumpTo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
@ -102,6 +102,7 @@ namespace MobiusEditor.Tools.Dialogs
//
// WaypointsToolDialog
//
this.AcceptButton = this.btnJumpTo;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;

View File

@ -33,7 +33,7 @@ namespace MobiusEditor.Tools
/// Layers that are not painted by the PostRenderMap function on ViewTool level because they are handled
/// at a specific point in the PostRenderMap override by the implementing tool.
/// </summary>
protected override MapLayerFlag ManuallyHandledLayers => MapLayerFlag.WaypointsIndic;
protected override MapLayerFlag ManuallyHandledLayers => MapLayerFlag.WaypointsIndic | MapLayerFlag.TechnoTriggers;
private readonly ComboBox waypointCombo;
private readonly Button jumpToButton;
@ -246,6 +246,9 @@ namespace MobiusEditor.Tools
case Keys.Up:
newVal = Math.Max(curVal - 1, 0);
break;
case Keys.Enter:
JumpToWaypoint();
break;
}
}
if (newVal.HasValue && curVal != newVal.Value)
@ -393,29 +396,37 @@ namespace MobiusEditor.Tools
private void JumpToButton_Click(Object sender, EventArgs e)
{
if (waypointCombo.SelectedItem is Waypoint wp)
JumpToWaypoint();
}
protected void JumpToWaypoint()
{
if (!(waypointCombo.SelectedItem is Waypoint wp))
{
int cell = wp.Cell.GetValueOrDefault(-1);
if (cell != -1)
{
Point cellPoint;
if (RenderMap.Metrics.GetLocation(cell, out cellPoint))
{
int scaleFull = Math.Min(mapPanel.ClientRectangle.Width, mapPanel.ClientRectangle.Height);
bool isWidth = scaleFull == mapPanel.ClientRectangle.Width;
double mapSize = isWidth ? map.Metrics.Width : map.Metrics.Height;
// pixels per tile at zoom level 1.
double basicTileSize = scaleFull / mapSize;
// Convert cell position to actual position on image.
int cellX = (int)Math.Round(basicTileSize * mapPanel.Zoom * (cellPoint.X + 0.5));
int cellY = (int)Math.Round(basicTileSize * mapPanel.Zoom * (cellPoint.Y + 0.5));
// Get location to use to center the waypoint on the screen.
int x = cellX - mapPanel.ClientRectangle.Width / 2;
int y = cellY - mapPanel.ClientRectangle.Height / 2;
mapPanel.AutoScrollPosition = new Point(x,y);
}
}
return;
}
int cell = wp.Cell.GetValueOrDefault(-1);
if (cell == -1)
{
return;
}
Point cellPoint;
if (!RenderMap.Metrics.GetLocation(cell, out cellPoint))
{
return;
}
int scaleFull = Math.Min(mapPanel.ClientRectangle.Width, mapPanel.ClientRectangle.Height);
bool isWidth = scaleFull == mapPanel.ClientRectangle.Width;
double mapSize = isWidth ? map.Metrics.Width : map.Metrics.Height;
// pixels per tile at zoom level 1.
double basicTileSize = scaleFull / mapSize;
// Convert cell position to actual position on image.
int cellX = (int)Math.Round(basicTileSize * mapPanel.Zoom * (cellPoint.X + 0.5));
int cellY = (int)Math.Round(basicTileSize * mapPanel.Zoom * (cellPoint.Y + 0.5));
// Get location to use to center the waypoint on the screen.
int x = cellX - mapPanel.ClientRectangle.Width / 2;
int y = cellY - mapPanel.ClientRectangle.Height / 2;
mapPanel.AutoScrollPosition = new Point(x, y);
}
protected override void PreRenderMap()
@ -458,6 +469,13 @@ namespace MobiusEditor.Tools
MapRenderer.RenderAllFootballAreas(graphics, map, Globals.MapTileSize, Globals.MapTileScale, plugin.GameType);
MapRenderer.RenderFootballAreaFlags(graphics, plugin.GameType, map, Globals.MapTileSize);
}
// If the selected waypoint is not a flag, re-render it as opaque.
if (selected != null && (plugin.Map.BasicSection.SoloMission || (selected.Flag & WaypointFlag.PlayerStart) != WaypointFlag.PlayerStart))
{
MapRenderer.Render(plugin.GameType, true, map.Theater, Globals.MapTileSize, map.FlagColors.ToArray(), selected, 1.0f).Item2(graphics);
}
// Render those here to they are put over the opaque redraw of the current waypoint.
MapRenderer.RenderAllTechnoTriggers(graphics, plugin.Map, Globals.MapTileSize, Globals.MapTileScale, Layers);
MapRenderer.RenderAllBoundsFromCell(graphics, Globals.MapTileSize,
map.Waypoints.Where(wp => wp != selected && wp.Cell.HasValue).Select(wp => wp.Cell.Value), map.Metrics, Color.Orange);
MapRenderer.RenderWayPointIndicators(graphics, map, Globals.MapTileSize, Globals.MapTileScale, Color.LightGreen, false, true, selectedRange);

View File

@ -116,6 +116,14 @@ namespace MobiusEditor.Utility
propertyValues.Clear();
}
public void SetValues(T valuesObject)
{
foreach (var propertyValue in trackableProperties)
{
TrySetMember(propertyValue.Key, trackableProperties[propertyValue.Key].GetValue(valuesObject));
}
}
public IDictionary<string, object> GetUndoValues() => propertyValues.ToDictionary(kv => kv.Key, kv => trackableProperties[kv.Key].GetValue(Object));
public IDictionary<string, object> GetRedoValues() => new Dictionary<string, object>(propertyValues);

View File

@ -109,8 +109,15 @@ namespace MobiusEditor.Utility
fps = 0;
tiles = null;
Tileset first = null;
foreach (var tileset in tilesets.Join(searchTilesets, x => x.Key, y => y, (x, y) => x.Value))
// Tilesets are now searched in the given order, allowing accurate defining of main tilesets and fallback tilesets.
//foreach (var tileset in tilesets.Join(searchTilesets, x => x.Key, y => y, (x, y) => x.Value))
foreach (string searchTileset in searchTilesets)
{
if (!tilesets.ContainsKey(searchTileset))
{
continue;
}
Tileset tileset = tilesets[searchTileset];
if (generateFallback && first == null)
{
first = tileset;

View File

@ -1,4 +1,4 @@
## C&C Tiberian Dawn and Red Alert Map Editor
## Mobius Map Editor
An enhanced version of the C&C Tiberian Dawn and Red Alert Map Editor based on the source code released by Electronic Arts. The goal of the project is simply to improve the usability and convenience of the map editor, fix bugs, improve and clean its code-base, enhance compatibility with different kinds of systems and enhance the editor's support for mods.
@ -12,7 +12,7 @@ Right now, I'm not really looking into making this a joint project. Specific bug
**DO NOT unpack this in the C&C Remastered Collection's install folder.** It is absolutely unnecessary to overwrite any files of the installed game.
Simply unpack the editor into a new folder on your disk somewhere. On first startup, it will automatically try to detect the folder in which the game is installed, and if it can't find it, it will show a popup asking you to locate it.
Simply unpack the editor into a new folder on your disk somewhere. On first startup, it will automatically try to detect the folder in which the game is installed, and if it can't find it, it will show a popup asking you to locate it. Note that this autodetect only works on Steam installations of the game.
---
@ -38,7 +38,7 @@ The file "CnCTDRAMapEditor.exe.config" contains settings to customise the editor
### Editor options:
* **ModsToLoad**: semicolon (or comma) separated list of mod entries. A mod entry can either be a Steam workshop ID, or a path of the type "Tiberian_Dawn\ModName" or "Red_Alert\ModName". The paths will initially be looked up under My Documents, but the loading system will also check the Steam workshop files, and use the game prefix part to verify the mod's targeted game. A mod will only be applied to the editor when a map of its targeted game is opened. Note that mods can only apply graphical changes from the tileset and house color xml files; the editor can't read any data from compiled dll files.
* **ModsToLoadTD** / **ModsToLoadRA** / **ModsToLoadSS**: semicolon (or comma) separated list of mod entries for each supported game. A mod entry can either be a Steam workshop ID, or a folder name. The paths will initially be looked up in the mods folder of the respective game in the CnCRemastered\mods\ folder under your Documents folder, but the loading system will also check the Steam workshop files for a matching mod. Sole Survivor will use Tiberian Dawn mods. Note that mods can only apply graphical changes from the tileset and house color xml files; the editor can't read any data from compiled dll files. This mods system is mostly meant to apply graphical fixes to the editor.
* **MapScale**: Scaling multiplier for the size at which assets are rendered on the map; higher means lower quality. This will make the UI more responsive. Negative values will enable smooth scaling, which gives nicer graphics but will make the UI noticeable _less_ responsive. Defaults to 0.5.
* **PreviewScale**: Scaling multiplier for the size at which assets are rendered on the preview tools. Negative values will enable smooth scaling, but this usually doesn't look good on the upscaled preview graphics. Defaults to 1.
* **ExportScale**: Scaling multiplier for the size at which an exported image will be generated through "Tools" → "Export As Image". Negative values will enable smooth scaling. Defaults to -0.5.
@ -47,7 +47,7 @@ The file "CnCTDRAMapEditor.exe.config" contains settings to customise the editor
* **MaxMapTileTextureSize**: Maximum for the size of the tiles shown on the Map tool. Leave on 0 to disable.
* **UndoRedoStackSize**: The amount of undo/redo actions stored in memory. Defaults to 50.
The **ModsToLoad** setting will have the `Tiberian_Dawn\ConcretePavementTD` mod set by default, to complete the incomplete TD Remastered graphics set, meaning it will automatically be loaded if found.
The **ModsToLoadTD** and **ModsToLoadSS** settings will have the `ConcretePavementTD` mod set by default, to complete the incomplete TD Remastered graphics set, meaning the mod will automatically be loaded if found. Note that the editor has no way to check whether mods are enabled in the game, so that makes no difference.
You can find the mod [on the Steam workshop](https://steamcommunity.com/sharedfiles/filedetails/?id=2844969675) and [on ModDB](https://www.moddb.com/games/command-conquer-remastered/addons/concretepavementtd).
@ -366,6 +366,14 @@ These options are all enabled by default, but can be disabled if you wish. Use t
* Changed the editor name in the title to "Mobius Map Editor".
* Red Alert maps are now specifically detected on the presence of the "[MapPack]" section. If this is not present, and there is no .bin file, it loads as TD map without map templates.
* Sole Survivor maps now don't support owned objects (infantry, units, structures) by default, though this can be re-enabled using the "NoOwnedObjectsInSole" setting in "CnCTDRAMapEditor.exe.config".
* Restricted Red Alert trigger and teamtype names to the same lengths as Tiberian Dawn; 4 for triggers, 8 for Teamtypes.
* Pressing [Enter] in Waypoints mode will now jump to the selected waypoint.
* Fixed a bug in the overlap detection system that made it always give "<unknown>" for the overlapped cell on Terrain objects.
* Split mods up into ModsToLoadTD, ModsToLoadRA and ModsToLoadSS.
* The Civilian buildings V12 and V13 (haystacks) are now also available in TD Winter theater.
* The trigger "Any: Cap=Win,Des=Lose" is now also seen as flag to autodetect classic single play scenarios.
* Like the game, the editor will now fall back to Temperate graphics when not finding the Winter graphics for the Haystack buildings/overlays.
* Fixed triggers being selectable on unbuilt buildings.
### Possible future features