TeamTypesDialog.cs (19496B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // The Command & Conquer Map Editor and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // The Command & Conquer Map Editor and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 using MobiusEditor.Interface; 16 using MobiusEditor.Model; 17 using MobiusEditor.Utility; 18 using System; 19 using System.Collections.Generic; 20 using System.Data; 21 using System.Linq; 22 using System.Windows.Forms; 23 24 namespace MobiusEditor.Dialogs 25 { 26 public partial class TeamTypesDialog : Form 27 { 28 private readonly IGamePlugin plugin; 29 private readonly int maxTeams; 30 private readonly IEnumerable<ITechnoType> technoTypes; 31 32 private readonly List<TeamType> teamTypes; 33 public IEnumerable<TeamType> TeamTypes => teamTypes; 34 35 private ListViewItem SelectedItem => (teamTypesListView.SelectedItems.Count > 0) ? teamTypesListView.SelectedItems[0] : null; 36 37 private TeamType SelectedTeamType => SelectedItem?.Tag as TeamType; 38 39 private TeamTypeClass mockClass; 40 private TeamTypeMission mockMission; 41 private int classEditRow = -1; 42 private int missionEditRow = -1; 43 44 public TeamTypesDialog(IGamePlugin plugin, int maxTeams) 45 { 46 this.plugin = plugin; 47 this.maxTeams = maxTeams; 48 technoTypes = plugin.Map.InfantryTypes.Cast<ITechnoType>().Concat(plugin.Map.UnitTypes.Cast<ITechnoType>()); 49 50 InitializeComponent(); 51 52 switch (plugin.GameType) 53 { 54 case GameType.TiberianDawn: 55 triggerLabel.Visible = triggerComboBox.Visible = false; 56 waypointLabel.Visible = waypointComboBox.Visible = false; 57 break; 58 case GameType.RedAlert: 59 learningCheckBox.Visible = false; 60 mercernaryCheckBox.Visible = false; 61 break; 62 } 63 64 teamTypes = new List<TeamType>(plugin.Map.TeamTypes.Select(t => t.Clone())); 65 66 teamTypesListView.BeginUpdate(); 67 { 68 foreach (var teamType in this.teamTypes) 69 { 70 var item = new ListViewItem(teamType.Name) 71 { 72 Tag = teamType 73 }; 74 teamTypesListView.Items.Add(item).ToolTipText = teamType.Name; 75 } 76 } 77 teamTypesListView.EndUpdate(); 78 79 houseComboBox.DataSource = plugin.Map.Houses.Select(t => new TypeItem<HouseType>(t.Type.Name, t.Type)).ToArray(); 80 waypointComboBox.DataSource = "(none)".Yield().Concat(plugin.Map.Waypoints.Select(w => w.Name)).ToArray(); 81 triggerComboBox.DataSource = Trigger.None.Yield().Concat(plugin.Map.Triggers.Select(t => t.Name)).ToArray(); 82 83 teamsTypeColumn.DisplayMember = "Name"; 84 teamsTypeColumn.ValueMember = "Type"; 85 teamsTypeColumn.DataSource = technoTypes.Select(t => new TypeItem<ITechnoType>(t.Name, t)).ToArray(); 86 87 missionsMissionColumn.DataSource = plugin.Map.TeamMissionTypes; 88 89 teamTypeTableLayoutPanel.Visible = false; 90 } 91 92 private void teamTypesListView_SelectedIndexChanged(object sender, EventArgs e) 93 { 94 houseComboBox.DataBindings.Clear(); 95 roundaboutCheckBox.DataBindings.Clear(); 96 learningCheckBox.DataBindings.Clear(); 97 suicideCheckBox.DataBindings.Clear(); 98 autocreateCheckBox.DataBindings.Clear(); 99 mercernaryCheckBox.DataBindings.Clear(); 100 reinforcableCheckBox.DataBindings.Clear(); 101 prebuiltCheckBox.DataBindings.Clear(); 102 recruitPriorityNud.DataBindings.Clear(); 103 initNumNud.DataBindings.Clear(); 104 maxAllowedNud.DataBindings.Clear(); 105 fearNud.DataBindings.Clear(); 106 waypointComboBox.DataBindings.Clear(); 107 triggerComboBox.DataBindings.Clear(); 108 109 if (SelectedTeamType != null) 110 { 111 houseComboBox.DataBindings.Add("SelectedValue", SelectedTeamType, "House"); 112 roundaboutCheckBox.DataBindings.Add("Checked", SelectedTeamType, "IsRoundAbout"); 113 learningCheckBox.DataBindings.Add("Checked", SelectedTeamType, "IsLearning"); 114 suicideCheckBox.DataBindings.Add("Checked", SelectedTeamType, "IsSuicide"); 115 autocreateCheckBox.DataBindings.Add("Checked", SelectedTeamType, "IsAutocreate"); 116 mercernaryCheckBox.DataBindings.Add("Checked", SelectedTeamType, "IsMercenary"); 117 reinforcableCheckBox.DataBindings.Add("Checked", SelectedTeamType, "IsReinforcable"); 118 prebuiltCheckBox.DataBindings.Add("Checked", SelectedTeamType, "IsPrebuilt"); 119 recruitPriorityNud.DataBindings.Add("Value", SelectedTeamType, "RecruitPriority"); 120 initNumNud.DataBindings.Add("Value", SelectedTeamType, "InitNum"); 121 maxAllowedNud.DataBindings.Add("Value", SelectedTeamType, "MaxAllowed"); 122 fearNud.DataBindings.Add("Value", SelectedTeamType, "Fear"); 123 waypointComboBox.DataBindings.Add("SelectedIndex", SelectedTeamType, "Origin"); 124 triggerComboBox.DataBindings.Add("SelectedItem", SelectedTeamType, "Trigger"); 125 126 mockClass = null; 127 mockMission = null; 128 classEditRow = -1; 129 missionEditRow = -1; 130 131 teamsDataGridView.Rows.Clear(); 132 missionsDataGridView.Rows.Clear(); 133 134 teamsDataGridView.RowCount = SelectedTeamType.Classes.Count + 1; 135 missionsDataGridView.RowCount = SelectedTeamType.Missions.Count + 1; 136 137 updateDataGridViewAddRows(teamsDataGridView, Globals.MaxTeamClasses); 138 updateDataGridViewAddRows(missionsDataGridView, Globals.MaxTeamMissions); 139 140 teamTypeTableLayoutPanel.Visible = true; 141 } 142 else 143 { 144 teamTypeTableLayoutPanel.Visible = false; 145 } 146 } 147 148 private void teamTypesListView_MouseDown(object sender, MouseEventArgs e) 149 { 150 if (e.Button == MouseButtons.Right) 151 { 152 var hitTest = teamTypesListView.HitTest(e.Location); 153 154 bool canAdd = (hitTest.Item == null) && (teamTypesListView.Items.Count < maxTeams); 155 bool canRemove = hitTest.Item != null; 156 addTeamTypeToolStripMenuItem.Visible = canAdd; 157 removeTeamTypeToolStripMenuItem.Visible = canRemove; 158 159 if (canAdd || canRemove) 160 { 161 teamTypesContextMenuStrip.Show(Cursor.Position); 162 } 163 } 164 } 165 166 private void teamTypesListView_KeyDown(object sender, KeyEventArgs e) 167 { 168 if ((e.KeyData == Keys.F2) && (teamTypesListView.SelectedItems.Count > 0)) 169 { 170 teamTypesListView.SelectedItems[0].BeginEdit(); 171 } 172 } 173 174 private void addTeamTypeToolStripMenuItem_Click(object sender, EventArgs e) 175 { 176 var nameChars = Enumerable.Range(97, 26).Concat(Enumerable.Range(48, 10)); 177 178 string name = string.Empty; 179 foreach (var nameChar in nameChars) 180 { 181 name = new string((char)nameChar, 4); 182 if (!teamTypes.Where(t => t.Equals(name)).Any()) 183 { 184 break; 185 } 186 } 187 188 var teamType = new TeamType { Name = name, House = plugin.Map.HouseTypes.First() }; 189 var item = new ListViewItem(teamType.Name) 190 { 191 Tag = teamType 192 }; 193 teamTypes.Add(teamType); 194 teamTypesListView.Items.Add(item).ToolTipText = teamType.Name; 195 196 item.Selected = true; 197 item.BeginEdit(); 198 } 199 200 private void removeTeamTypeToolStripMenuItem_Click(object sender, EventArgs e) 201 { 202 if (SelectedItem != null) 203 { 204 teamTypes.Remove(SelectedTeamType); 205 teamTypesListView.Items.Remove(SelectedItem); 206 } 207 } 208 209 private void teamTypesListView_AfterLabelEdit(object sender, LabelEditEventArgs e) 210 { 211 int maxLength = int.MaxValue; 212 switch (plugin.GameType) 213 { 214 case GameType.TiberianDawn: 215 maxLength = 8; 216 break; 217 case GameType.RedAlert: 218 maxLength = 23; 219 break; 220 } 221 222 if (string.IsNullOrEmpty(e.Label)) 223 { 224 e.CancelEdit = true; 225 } 226 else if (e.Label.Length > maxLength) 227 { 228 e.CancelEdit = true; 229 MessageBox.Show(string.Format("Team name is longer than {0} characters.", maxLength), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 230 } 231 else if (teamTypes.Where(t => (t != SelectedTeamType) && t.Equals(e.Label)).Any()) 232 { 233 e.CancelEdit = true; 234 MessageBox.Show(string.Format("Team with name '{0]' already exists", e.Label), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 235 } 236 else 237 { 238 SelectedTeamType.Name = e.Label; 239 teamTypesListView.Items[e.Item].ToolTipText = SelectedTeamType.Name; 240 } 241 } 242 243 private void teamsDataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 244 { 245 if (SelectedTeamType == null) 246 { 247 return; 248 } 249 250 TeamTypeClass teamTypeClass = null; 251 if (e.RowIndex == classEditRow) 252 { 253 teamTypeClass = mockClass; 254 } 255 else if (e.RowIndex < SelectedTeamType.Classes.Count) 256 { 257 teamTypeClass = SelectedTeamType.Classes[e.RowIndex]; 258 } 259 260 if (teamTypeClass == null) 261 { 262 return; 263 } 264 265 switch (e.ColumnIndex) 266 { 267 case 0: 268 e.Value = teamTypeClass.Type; 269 break; 270 case 1: 271 e.Value = teamTypeClass.Count; 272 break; 273 } 274 } 275 276 private void teamsDataGridView_CellValuePushed(object sender, DataGridViewCellValueEventArgs e) 277 { 278 if (SelectedTeamType == null) 279 { 280 return; 281 } 282 283 if (mockClass == null) 284 { 285 mockClass = (e.RowIndex < SelectedTeamType.Classes.Count) ? 286 new TeamTypeClass { Type = SelectedTeamType.Classes[e.RowIndex].Type, Count = SelectedTeamType.Classes[e.RowIndex].Count } : 287 new TeamTypeClass { Type = technoTypes.First(), Count = 0 }; 288 } 289 classEditRow = e.RowIndex; 290 291 switch (e.ColumnIndex) 292 { 293 case 0: 294 mockClass.Type = e.Value as ITechnoType; 295 break; 296 case 1: 297 mockClass.Count = int.TryParse(e.Value as string, out int value) ? (byte)Math.Max(0, Math.Min(255, value)) : (byte)0; 298 break; 299 } 300 } 301 302 private void teamsDataGridView_NewRowNeeded(object sender, DataGridViewRowEventArgs e) 303 { 304 mockClass = new TeamTypeClass { Type = technoTypes.First(), Count = 0 }; 305 classEditRow = teamsDataGridView.RowCount - 1; 306 } 307 308 private void teamsDataGridView_RowValidated(object sender, DataGridViewCellEventArgs e) 309 { 310 if ((mockClass != null) && (e.RowIndex >= SelectedTeamType.Classes.Count) && ((teamsDataGridView.Rows.Count > 1) || (e.RowIndex < (teamsDataGridView.Rows.Count - 1)))) 311 { 312 SelectedTeamType.Classes.Add(mockClass); 313 mockClass = null; 314 classEditRow = -1; 315 } 316 else if ((mockClass != null) && (e.RowIndex < SelectedTeamType.Classes.Count)) 317 { 318 SelectedTeamType.Classes[e.RowIndex] = mockClass; 319 mockClass = null; 320 classEditRow = -1; 321 } 322 else if (teamsDataGridView.ContainsFocus) 323 { 324 mockClass = null; 325 classEditRow = -1; 326 } 327 } 328 329 private void teamsDataGridView_RowDirtyStateNeeded(object sender, QuestionEventArgs e) 330 { 331 e.Response = teamsDataGridView.IsCurrentCellDirty; 332 } 333 334 private void teamsDataGridView_CancelRowEdit(object sender, QuestionEventArgs e) 335 { 336 if ((classEditRow == (teamsDataGridView.Rows.Count - 2)) && (classEditRow == SelectedTeamType.Classes.Count)) 337 { 338 mockClass = new TeamTypeClass { Type = technoTypes.First(), Count = 0 }; 339 } 340 else 341 { 342 mockClass = null; 343 classEditRow = -1; 344 } 345 } 346 347 private void teamsDataGridView_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) 348 { 349 if (e.Row.Index < SelectedTeamType.Classes.Count) 350 { 351 SelectedTeamType.Classes.RemoveAt(e.Row.Index); 352 } 353 354 if (e.Row.Index == classEditRow) 355 { 356 mockClass = null; 357 classEditRow = -1; 358 } 359 } 360 361 private void teamsDataGridView_UserAddedRow(object sender, DataGridViewRowEventArgs e) 362 { 363 updateDataGridViewAddRows(teamsDataGridView, Globals.MaxTeamClasses); 364 } 365 366 private void teamsDataGridView_UserDeletedRow(object sender, DataGridViewRowEventArgs e) 367 { 368 updateDataGridViewAddRows(teamsDataGridView, Globals.MaxTeamClasses); 369 } 370 371 private void missionsDataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 372 { 373 if (SelectedTeamType == null) 374 { 375 return; 376 } 377 378 TeamTypeMission teamMissionType = null; 379 if (e.RowIndex == missionEditRow) 380 { 381 teamMissionType = mockMission; 382 } 383 else if (e.RowIndex < SelectedTeamType.Missions.Count) 384 { 385 teamMissionType = SelectedTeamType.Missions[e.RowIndex]; 386 } 387 388 if (teamMissionType == null) 389 { 390 return; 391 } 392 393 switch (e.ColumnIndex) 394 { 395 case 0: 396 e.Value = teamMissionType.Mission; 397 break; 398 case 1: 399 e.Value = teamMissionType.Argument; 400 break; 401 } 402 } 403 404 private void missionsDataGridView_CellValuePushed(object sender, DataGridViewCellValueEventArgs e) 405 { 406 if (SelectedTeamType == null) 407 { 408 return; 409 } 410 411 if (mockMission == null) 412 { 413 mockMission = (e.RowIndex < SelectedTeamType.Missions.Count) ? 414 new TeamTypeMission { Mission = SelectedTeamType.Missions[e.RowIndex].Mission, Argument = SelectedTeamType.Missions[e.RowIndex].Argument } : 415 new TeamTypeMission { Mission = plugin.Map.TeamMissionTypes.First(), Argument = 0 }; 416 } 417 missionEditRow = e.RowIndex; 418 419 switch (e.ColumnIndex) 420 { 421 case 0: 422 mockMission.Mission = e.Value as string; 423 break; 424 case 1: 425 mockMission.Argument = int.TryParse(e.Value as string, out int value) ? value : 0; 426 break; 427 } 428 } 429 430 private void missionsDataGridView_NewRowNeeded(object sender, DataGridViewRowEventArgs e) 431 { 432 mockMission = new TeamTypeMission { Mission = plugin.Map.TeamMissionTypes.First(), Argument = 0 }; 433 missionEditRow = missionsDataGridView.RowCount - 1; 434 } 435 436 private void missionsDataGridView_RowValidated(object sender, DataGridViewCellEventArgs e) 437 { 438 if ((mockMission != null) && (e.RowIndex >= SelectedTeamType.Missions.Count) && ((missionsDataGridView.Rows.Count > 1) || (e.RowIndex < (missionsDataGridView.Rows.Count - 1)))) 439 { 440 SelectedTeamType.Missions.Add(mockMission); 441 mockMission = null; 442 missionEditRow = -1; 443 } 444 else if ((mockMission != null) && (e.RowIndex < SelectedTeamType.Missions.Count)) 445 { 446 SelectedTeamType.Missions[e.RowIndex] = mockMission; 447 mockMission = null; 448 missionEditRow = -1; 449 } 450 else if (missionsDataGridView.ContainsFocus) 451 { 452 mockMission = null; 453 missionEditRow = -1; 454 } 455 } 456 457 private void missionsDataGridView_RowDirtyStateNeeded(object sender, QuestionEventArgs e) 458 { 459 e.Response = missionsDataGridView.IsCurrentCellDirty; 460 } 461 462 private void missionsDataGridView_CancelRowEdit(object sender, QuestionEventArgs e) 463 { 464 if ((missionEditRow == (missionsDataGridView.Rows.Count - 2)) && (missionEditRow == SelectedTeamType.Missions.Count)) 465 { 466 mockMission = new TeamTypeMission { Mission = plugin.Map.TeamMissionTypes.First(), Argument = 0 }; 467 } 468 else 469 { 470 mockMission = null; 471 missionEditRow = -1; 472 } 473 } 474 475 private void missionsDataGridView_UserDeletingRow(object sender, DataGridViewRowCancelEventArgs e) 476 { 477 if (e.Row.Index < SelectedTeamType.Missions.Count) 478 { 479 SelectedTeamType.Missions.RemoveAt(e.Row.Index); 480 } 481 482 if (e.Row.Index == missionEditRow) 483 { 484 mockMission = null; 485 missionEditRow = -1; 486 } 487 } 488 489 private void missionsDataGridView_UserAddedRow(object sender, DataGridViewRowEventArgs e) 490 { 491 updateDataGridViewAddRows(missionsDataGridView, Globals.MaxTeamMissions); 492 } 493 494 private void missionsDataGridView_UserDeletedRow(object sender, DataGridViewRowEventArgs e) 495 { 496 updateDataGridViewAddRows(missionsDataGridView, Globals.MaxTeamMissions); 497 } 498 499 private void updateDataGridViewAddRows(DataGridView dataGridView, int maxItems) 500 { 501 dataGridView.AllowUserToAddRows = dataGridView.Rows.Count <= maxItems; 502 } 503 } 504 }