diff --git a/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj b/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj index 79de089..e8138a3 100644 --- a/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj +++ b/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj @@ -461,7 +461,9 @@ + + diff --git a/CnCTDRAMapEditor/Controls/BasicSettings.cs b/CnCTDRAMapEditor/Controls/BasicSettings.cs index 4ff74af..ced9fda 100644 --- a/CnCTDRAMapEditor/Controls/BasicSettings.cs +++ b/CnCTDRAMapEditor/Controls/BasicSettings.cs @@ -55,8 +55,6 @@ namespace MobiusEditor.Controls percentLabel.Visible = false; playerComboBox.DataBindings.Add("SelectedItem", basicSection, "Player"); authorTxt.DataBindings.Add("Text", basicSection, "Author"); - isSinglePlayerCheckBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged; - isSinglePlayerCheckBox.DataBindings.Add("Checked", basicSection, "SoloMission"); themeComboBox.DataBindings.Add("Text", basicSection, "Theme"); introComboBox.DataBindings.Add("Text", basicSection, "Intro"); briefComboBox.DataBindings.Add("Text", basicSection, "Brief"); @@ -69,6 +67,8 @@ namespace MobiusEditor.Controls switch (plugin.GameType) { case GameType.TiberianDawn: + isSinglePlayerCheckBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged; + isSinglePlayerCheckBox.DataBindings.Add("Checked", basicSection, "SoloMission"); buildLevelNud.DataBindings.Add("Value", basicSection, "BuildLevel"); baseLabel.Visible = baseComboBox.Visible = false; hasExpansionUnitsCheckBox.Visible = false; @@ -80,15 +80,38 @@ namespace MobiusEditor.Controls win3ComboBox.DropDownStyle = ComboBoxStyle.DropDown; win4ComboBox.DropDownStyle = ComboBoxStyle.DropDown; loseComboBox.DropDownStyle = ComboBoxStyle.DropDown; + CheckSinglePlayerOptions(); break; case GameType.RedAlert: + isSinglePlayerCheckBox.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged; + isSinglePlayerCheckBox.DataBindings.Add("Checked", basicSection, "SoloMission"); buildLevelNud.Visible = buildLevelLabel.Visible = false; baseComboBox.DataBindings.Add("SelectedItem", basicSection, "BasePlayer"); hasExpansionUnitsCheckBox.DataBindings.Add("Checked", basicSection, "ExpansionEnabled"); + CheckSinglePlayerOptions(); + break; + case GameType.SoleSurvivor: + isSinglePlayerCheckBox.Visible = false; + buildLevelNud.DataBindings.Add("Value", basicSection, "BuildLevel"); + baseLabel.Visible = baseComboBox.Visible = false; + hasExpansionUnitsCheckBox.Visible = false; + introComboBox.DropDownStyle = ComboBoxStyle.DropDown; + briefComboBox.DropDownStyle = ComboBoxStyle.DropDown; + actionComboBox.DropDownStyle = ComboBoxStyle.DropDown; + winComboBox.DropDownStyle = ComboBoxStyle.DropDown; + // Irrelevant for SS; it's all classic. + lblCarryoverClassic.Visible = false; + lblThemeClassic.Visible = false; + // Disable these. Labels and stuff too. + win2ComboBox.Visible = false; + win3ComboBox.Visible = false; + win4ComboBox.Visible = false; + loseComboBox.Visible = false; + + + break; } - - CheckSinglePlayerOptions(); } private void isSinglePlayerCheckBox_CheckedChanged(object sender, EventArgs e) diff --git a/CnCTDRAMapEditor/Controls/ObjectProperties.cs b/CnCTDRAMapEditor/Controls/ObjectProperties.cs index 92a402a..40ea866 100644 --- a/CnCTDRAMapEditor/Controls/ObjectProperties.cs +++ b/CnCTDRAMapEditor/Controls/ObjectProperties.cs @@ -206,27 +206,39 @@ namespace MobiusEditor.Controls directionLabel.Visible = directionVisible; directionComboBox.Visible = directionVisible; missionLabel.Visible = missionComboBox.Visible = false; - basePriorityLabel.Visible = basePriorityNud.Visible = true; - prebuiltCheckBox.Visible = true; - prebuiltCheckBox.Enabled = building.BasePriority >= 0; - - basePriorityNud.DataBindings.Add("Value", obj, "BasePriority"); - prebuiltCheckBox.DataBindings.Add("Checked", obj, "IsPrebuilt"); - switch (Plugin.GameType) { case GameType.TiberianDawn: { + basePriorityLabel.Visible = basePriorityNud.Visible = true; + prebuiltCheckBox.Visible = true; + prebuiltCheckBox.Enabled = building.BasePriority >= 0; + basePriorityNud.DataBindings.Add("Value", obj, "BasePriority"); + prebuiltCheckBox.DataBindings.Add("Checked", obj, "IsPrebuilt"); sellableCheckBox.Visible = false; rebuildCheckBox.Visible = false; } break; case GameType.RedAlert: { + basePriorityLabel.Visible = basePriorityNud.Visible = true; + prebuiltCheckBox.Visible = true; + prebuiltCheckBox.Enabled = building.BasePriority >= 0; + basePriorityNud.DataBindings.Add("Value", obj, "BasePriority"); + prebuiltCheckBox.DataBindings.Add("Checked", obj, "IsPrebuilt"); sellableCheckBox.DataBindings.Add("Checked", obj, "Sellable"); rebuildCheckBox.DataBindings.Add("Checked", obj, "Rebuild"); sellableCheckBox.Visible = true; rebuildCheckBox.Visible = true; - } break; + } + break; + case GameType.SoleSurvivor: + { + basePriorityLabel.Visible = basePriorityNud.Visible = false; + prebuiltCheckBox.Visible = false; + sellableCheckBox.Visible = false; + rebuildCheckBox.Visible = false; + } + break; } } break; diff --git a/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs b/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs index a5b9c73..d265d58 100644 --- a/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs +++ b/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs @@ -106,6 +106,14 @@ namespace MobiusEditor.Dialogs } settingsTreeView.Nodes.Add("RULES", "Rules"); } + else + { + if (this.plugin.GameType == GameType.SoleSurvivor) + { + settingsTreeView.Nodes.Add("CRATES", "Crates"); + } + settingsTreeView.Nodes.Add("RULES", "Extra ini contents"); + } settingsTreeView.Nodes.Add("BRIEFING", "Briefing"); playersNode = settingsTreeView.Nodes.Add("Players"); foreach (var player in this.plugin.Map.Houses) @@ -150,6 +158,14 @@ namespace MobiusEditor.Dialogs basicPanel.Dock = DockStyle.Fill; } break; + case "CRATES": + { + // TODO make crates setting screen for SS. + ScenarioSettings scenPanel = new ScenarioSettings(basicSettingsTracker); + settingsPanel.Controls.Add(scenPanel); + scenPanel.Dock = DockStyle.Fill; + } + break; case "SCENARIO": { ScenarioSettings scenPanel = new ScenarioSettings(basicSettingsTracker); diff --git a/CnCTDRAMapEditor/Interface/IGamePlugin.cs b/CnCTDRAMapEditor/Interface/IGamePlugin.cs index 75fb20c..b2c1705 100644 --- a/CnCTDRAMapEditor/Interface/IGamePlugin.cs +++ b/CnCTDRAMapEditor/Interface/IGamePlugin.cs @@ -13,7 +13,6 @@ // GNU General Public License along with permitted additional restrictions // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection using MobiusEditor.Model; -using MobiusEditor.Utility; using System; using System.Collections.Generic; using System.Drawing; @@ -33,7 +32,8 @@ namespace MobiusEditor.Interface { None, TiberianDawn, - RedAlert + RedAlert, + SoleSurvivor } public interface IGamePlugin : IDisposable diff --git a/CnCTDRAMapEditor/MainForm.cs b/CnCTDRAMapEditor/MainForm.cs index efe3495..1cf2ff0 100644 --- a/CnCTDRAMapEditor/MainForm.cs +++ b/CnCTDRAMapEditor/MainForm.cs @@ -876,15 +876,15 @@ namespace MobiusEditor switch (fileType) { case FileType.INI: - { - gameType = File.Exists(Path.ChangeExtension(loadFilename, ".bin")) ? GameType.TiberianDawn : GameType.RedAlert; - break; - } + { + gameType = File.Exists(Path.ChangeExtension(loadFilename, ".bin")) ? GameType.TiberianDawn : GameType.RedAlert; + break; + } case FileType.BIN: - { - gameType = File.Exists(Path.ChangeExtension(loadFilename, ".ini")) ? GameType.TiberianDawn : GameType.None; - break; - } + { + gameType = File.Exists(Path.ChangeExtension(loadFilename, ".ini")) ? GameType.TiberianDawn : GameType.None; + break; + } #if DEVELOPER case FileType.PGM: { @@ -910,6 +910,13 @@ namespace MobiusEditor } #endif } + if (gameType == GameType.TiberianDawn) + { + if (TiberianDawn.GamePluginSS.CheckForSSmap(loadFilename, fileType)) + { + gameType = GameType.SoleSurvivor; + } + } if (gameType == GameType.None) { return false; @@ -923,7 +930,12 @@ namespace MobiusEditor string[] modPaths = null; if (ModPaths != null) { - ModPaths.TryGetValue(gameType, out modPaths); + GameType modGameType = gameType; + if (gameType == GameType.SoleSurvivor) + { + modGameType = GameType.TiberianDawn; + } + ModPaths.TryGetValue(modGameType, out modPaths); } Globals.TheTextureManager.ExpandModPaths = modPaths; Globals.TheTextureManager.Reset(); @@ -947,6 +959,11 @@ namespace MobiusEditor plugin = new RedAlert.GamePlugin(this); } break; + case GameType.SoleSurvivor: + Globals.TheTeamColorManager.Reset(); + Globals.TheTeamColorManager.Load(@"DATA\XML\CNCTDTEAMCOLORS.XML"); + plugin = new TiberianDawn.GamePluginSS(this); + break; } string[] errors; bool modifiedByLoad; diff --git a/CnCTDRAMapEditor/Model/Waypoint.cs b/CnCTDRAMapEditor/Model/Waypoint.cs index 9b81b4d..ab7efb6 100644 --- a/CnCTDRAMapEditor/Model/Waypoint.cs +++ b/CnCTDRAMapEditor/Model/Waypoint.cs @@ -26,7 +26,9 @@ namespace MobiusEditor.Model Flare = 1 << 1, Home = 1 << 2, Reinforce = 1 << 3, - Special = 1 << 4 + Special = 1 << 4, + CrateSpawn = 1 << 5, + } public class Waypoint : INamedType diff --git a/CnCTDRAMapEditor/Render/MapRenderer.cs b/CnCTDRAMapEditor/Render/MapRenderer.cs index fbfa967..ec903f5 100644 --- a/CnCTDRAMapEditor/Render/MapRenderer.cs +++ b/CnCTDRAMapEditor/Render/MapRenderer.cs @@ -409,7 +409,7 @@ namespace MobiusEditor.Render if (building.Strength <= 128 && !building.Type.IsSingleFrame) { maxIcon = Globals.TheTilesetManager.GetTileDataLength(theater.Tilesets, building.Type.Tilename); - hasCollapseFrame = gameType == GameType.TiberianDawn && maxIcon > 1 && maxIcon % 2 == 1; + hasCollapseFrame = (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor) && maxIcon > 1 && maxIcon % 2 == 1; damageIcon = maxIcon / 2; collapseIcon = hasCollapseFrame ? maxIcon - 1 : damageIcon; } @@ -572,7 +572,7 @@ namespace MobiusEditor.Render public static (Rectangle, Action) Render(GameType gameType, TheaterType theater, Point topLeft, Size tileSize, Unit unit) { int icon = 0; - if (gameType == GameType.TiberianDawn) + if (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor) { if ((unit.Type == TiberianDawn.UnitTypes.Tric) || (unit.Type == TiberianDawn.UnitTypes.Trex) || @@ -745,7 +745,7 @@ namespace MobiusEditor.Render turretAdjust.Y = -5; } } - else if (gameType == GameType.TiberianDawn) + else if (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor) { if (unit.Type == TiberianDawn.UnitTypes.Jeep || unit.Type == TiberianDawn.UnitTypes.Buggy || diff --git a/CnCTDRAMapEditor/TiberianDawn/BuildingTypes.cs b/CnCTDRAMapEditor/TiberianDawn/BuildingTypes.cs index 50ed345..97d0427 100644 --- a/CnCTDRAMapEditor/TiberianDawn/BuildingTypes.cs +++ b/CnCTDRAMapEditor/TiberianDawn/BuildingTypes.cs @@ -39,7 +39,7 @@ namespace MobiusEditor.TiberianDawn public static readonly BuildingType Barracks = new BuildingType(15, "pyle", "TEXT_STRUCTURE_TITLE_GDI_BARRACKS", 0, 20, new bool[2, 2] { { true, true }, { false, false } }, "Goodguy", BuildingTypeFlag.Bib); public static readonly BuildingType Tanker = new BuildingType(16, "arco", "Oil Tanker", 0, 0, new bool[1, 2] { { true, true } }, "Neutral"); public static readonly BuildingType Repair = new BuildingType(17, "fix", "TEXT_STRUCTURE_TITLE_GDI_REPAIR_FACILITY", 0, 30, new bool[3, 3] { { false, true, false }, { true, true, true }, { false, true, false} }, "Goodguy", BuildingTypeFlag.Bib); - public static readonly BuildingType BioLab = new BuildingType(18, "bio", "TEXT_UNIT_TITLE_BIO", 0, 40, 100, new bool[2, 2] { { true, true }, { true, true } }, "Goodguy", BuildingTypeFlag.Bib); + public static readonly BuildingType BioLab = new BuildingType(18, "bio", "TEXT_UNIT_TITLE_BIO", 0, 40, 100, new bool[2, 2] { { true, true }, { true, true } }, "Badguy", BuildingTypeFlag.Bib); public static readonly BuildingType Hand = new BuildingType(19, "hand", "TEXT_STRUCTURE_TITLE_NOD_HAND_OF_NOD", 0, 20, new bool[3, 2] { { false, false }, { true, true }, { false, true } }, "Badguy", BuildingTypeFlag.Bib); public static readonly BuildingType Temple = new BuildingType(20, "tmpl", "TEXT_STRUCTURE_TITLE_NOD_TEMPLE_OF_NOD", 0, 150, new bool[3, 3] { { false, false, false }, { true, true, true }, { true, true, true } }, "Badguy", BuildingTypeFlag.Bib); public static readonly BuildingType Eye = new BuildingType(21, "eye", "TEXT_STRUCTURE_TITLE_GDI_ADV_COMM_CENTER", 0, 200, new bool[2, 2] { { true, false }, { true, true } }, "Goodguy", BuildingTypeFlag.Bib); diff --git a/CnCTDRAMapEditor/TiberianDawn/GamePlugin.cs b/CnCTDRAMapEditor/TiberianDawn/GamePlugin.cs index 8ed4295..3fda0a7 100644 --- a/CnCTDRAMapEditor/TiberianDawn/GamePlugin.cs +++ b/CnCTDRAMapEditor/TiberianDawn/GamePlugin.cs @@ -31,18 +31,18 @@ namespace MobiusEditor.TiberianDawn { public class GamePlugin : IGamePlugin { - private bool isMegaMap = false; + protected bool isMegaMap = false; - private const int multiStartPoints = 8; - private static readonly Regex SinglePlayRegex = new Regex("^SC[A-LN-RT-Z]\\d{2}[EWX][A-EL]$", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly Regex MovieRegex = new Regex(@"^(?:.*?\\)*(.*?)\.BK2$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + protected const int multiStartPoints = 8; + protected static readonly Regex SinglePlayRegex = new Regex("^SC[A-LN-RT-Z]\\d{2}[EWX][A-EL]$", RegexOptions.IgnoreCase | RegexOptions.Compiled); + protected static readonly Regex MovieRegex = new Regex(@"^(?:.*?\\)*(.*?)\.BK2$", RegexOptions.IgnoreCase | RegexOptions.Compiled); - private static readonly IEnumerable fullTechnoTypes; + protected static readonly IEnumerable fullTechnoTypes; - private const string defVidVal = "x"; - private readonly IEnumerable movieTypes; + protected const string defVidVal = "x"; + protected readonly IEnumerable movieTypes; - private static readonly IEnumerable movieTypesAdditional = new string[] + protected static readonly IEnumerable movieTypesAdditional = new string[] { "BODYBAGS (Classic only)", "REFINT (Classic only)", @@ -53,7 +53,7 @@ namespace MobiusEditor.TiberianDawn "TRTKIL_D (Classic only)", }; - private static readonly IEnumerable themeTypes = new string[] + protected static readonly IEnumerable themeTypes = new string[] { "No Theme", "AIRSTRIK", @@ -100,11 +100,11 @@ namespace MobiusEditor.TiberianDawn public GameType GameType => GameType.TiberianDawn; - public Map Map { get; } + public Map Map { get; protected set; } - public Image MapImage { get; private set; } + public Image MapImage { get; protected set; } - IFeedBackHandler feedBackHandler; + protected IFeedBackHandler feedBackHandler; bool isDirty; public bool Dirty @@ -113,7 +113,7 @@ namespace MobiusEditor.TiberianDawn set { isDirty = value; feedBackHandler?.UpdateStatus(); } } - private INISectionCollection extraSections; + protected INISectionCollection extraSections; public String ExtraIniText { get @@ -161,9 +161,15 @@ namespace MobiusEditor.TiberianDawn } public static bool CheckForMegamap(String path, FileType fileType) + { + return CheckForIniInfo(path, fileType, "Map", "Version", "1"); + } + + public static bool CheckForIniInfo(String path, FileType fileType, string section, string key, string value) { try { + Encoding enc = new UTF8Encoding(false, true); String iniContents = null; switch (fileType) { @@ -171,7 +177,6 @@ namespace MobiusEditor.TiberianDawn case FileType.BIN: String iniPath = fileType == FileType.INI ? path : Path.ChangeExtension(path, ".ini"); Byte[] bytes = File.ReadAllBytes(path); - Encoding enc = new UTF8Encoding(false, true); iniContents = enc.GetString(bytes); break; case FileType.MEG: @@ -181,7 +186,7 @@ namespace MobiusEditor.TiberianDawn var testIniFile = megafile.Where(p => Path.GetExtension(p).ToLower() == ".ini").FirstOrDefault(); if (testIniFile != null) { - using (var iniReader = new StreamReader(megafile.Open(testIniFile))) + using (var iniReader = new StreamReader(megafile.Open(testIniFile), enc)) { iniContents = iniReader.ReadToEnd(); } @@ -195,8 +200,12 @@ namespace MobiusEditor.TiberianDawn } INI checkIni = new INI(); checkIni.Parse(iniContents); - INISection mapSection = checkIni.Sections.Extract("Map"); - return mapSection.Keys.Contains("Version") && mapSection["Version"].Trim() == "1"; + INISection iniSection = checkIni.Sections.Extract(section); + if (key == null || value == null) + { + return iniSection != null; + } + return iniSection != null && iniSection.Keys.Contains(key) && iniSection[key].Trim() == value; } catch { @@ -214,14 +223,9 @@ namespace MobiusEditor.TiberianDawn { } - public GamePlugin(bool mapImage, bool megaMap, IFeedBackHandler feedBackHandler) + public GamePlugin() { - this.isMegaMap = megaMap; - this.feedBackHandler = feedBackHandler; - var playerWaypoints = Enumerable.Range(0, multiStartPoints).Select(i => new Waypoint(string.Format("P{0}", i), WaypointFlag.PlayerStart)); - var generalWaypoints = Enumerable.Range(multiStartPoints, 25 - multiStartPoints).Select(i => new Waypoint(i.ToString())); - var specialWaypoints = new Waypoint[] { new Waypoint("Flare", WaypointFlag.Flare), new Waypoint("Home", WaypointFlag.Home), new Waypoint("Reinf.", WaypointFlag.Reinforce) }; - var waypoints = playerWaypoints.Concat(generalWaypoints).Concat(specialWaypoints); + // Readonly, so I'm splitting this off var movies = new List(); using (var megafile = new Megafile(Path.Combine(Globals.MegafilePath, "MOVIES_TD.MEG"))) { @@ -239,6 +243,17 @@ namespace MobiusEditor.TiberianDawn movies.Sort(new ExplorerComparer()); movies.Insert(0, defVidVal); movieTypes = movies.ToArray(); + } + + public GamePlugin(bool mapImage, bool megaMap, IFeedBackHandler feedBackHandler) + :this() + { + this.isMegaMap = megaMap; + this.feedBackHandler = feedBackHandler; + var playerWaypoints = Enumerable.Range(0, multiStartPoints).Select(i => new Waypoint(string.Format("P{0}", i), WaypointFlag.PlayerStart)); + var generalWaypoints = Enumerable.Range(multiStartPoints, 25 - multiStartPoints).Select(i => new Waypoint(i.ToString())); + var specialWaypoints = new Waypoint[] { new Waypoint("Flare", WaypointFlag.Flare), new Waypoint("Home", WaypointFlag.Home), new Waypoint("Reinf.", WaypointFlag.Reinforce) }; + Waypoint[] waypoints = playerWaypoints.Concat(generalWaypoints).Concat(specialWaypoints).ToArray(); var basicSection = new BasicSection(); basicSection.SetDefault(); var houseTypes = HouseTypes.GetTypes(); @@ -249,7 +264,6 @@ namespace MobiusEditor.TiberianDawn EventTypes.EVENT_PLAYER_ENTERED, EventTypes.EVENT_NONE }; - string[] unitEventTypes = { EventTypes.EVENT_DISCOVERED, @@ -371,7 +385,7 @@ namespace MobiusEditor.TiberianDawn /// /// Stream reader to read from. /// The ini file as string, with all double ROAD overlay lines replaced by the dummy Road2 type. - private string FixRoad2Load(StreamReader iniReader) + protected string FixRoad2Load(StreamReader iniReader) { // ROAD's second state can only be accessed by applying ROAD overlay to the same cell twice. // This can be achieved by saving its Overlay line twice in the ini file. However, this is @@ -482,7 +496,7 @@ namespace MobiusEditor.TiberianDawn return sb.ToString(); } - private IEnumerable LoadINI(INI ini, bool forceSoloMission) + protected IEnumerable LoadINI(INI ini, bool forceSoloMission) { var errors = new List(); Map.BeginUpdate(); @@ -1473,7 +1487,7 @@ namespace MobiusEditor.TiberianDawn return errors; } - private IEnumerable LoadBinaryClassic(BinaryReader reader) + protected IEnumerable LoadBinaryClassic(BinaryReader reader) { var errors = new List(); Map.Templates.Clear(); @@ -1490,7 +1504,7 @@ namespace MobiusEditor.TiberianDawn return errors; } - private IEnumerable LoadBinaryMega(BinaryReader reader) + protected IEnumerable LoadBinaryMega(BinaryReader reader) { var errors = new List(); Map.Templates.Clear(); @@ -1518,7 +1532,7 @@ namespace MobiusEditor.TiberianDawn return errors; } - private TemplateType ChecKTemplateType(int typeValue, int iconValue, int x, int y, List errors) + protected TemplateType ChecKTemplateType(int typeValue, int iconValue, int x, int y, List errors) { TemplateType templateType = Map.TemplateTypes.Where(t => t.Equals(typeValue)).FirstOrDefault(); // Prevent loading of illegal tiles. @@ -1672,7 +1686,7 @@ namespace MobiusEditor.TiberianDawn /// /// The generated ini file /// The stream writer to write the text to. - private void FixRoad2Save(INI ini, StreamWriter iniWriter) + protected void FixRoad2Save(INI ini, StreamWriter iniWriter) { // ROAD's second state can only be accessed by applying ROAD overlay to the same cell twice. // This can be achieved by saving its Overlay line twice in the ini file. However, this is @@ -1709,7 +1723,7 @@ namespace MobiusEditor.TiberianDawn } } - private void SaveINI(INI ini, FileType fileType, string fileName) + protected void SaveINI(INI ini, FileType fileType, string fileName) { if (extraSections != null) { @@ -1962,7 +1976,7 @@ namespace MobiusEditor.TiberianDawn } } - private void SaveBinaryClassic(BinaryWriter writer) + protected void SaveBinaryClassic(BinaryWriter writer) { for (var y = 0; y < Map.Metrics.Height; ++y) { @@ -1983,7 +1997,7 @@ namespace MobiusEditor.TiberianDawn } } - private void SaveBinaryMega(BinaryWriter writer) + protected void SaveBinaryMega(BinaryWriter writer) { int height = Map.Metrics.Height; int width = Map.Metrics.Width; @@ -2005,12 +2019,12 @@ namespace MobiusEditor.TiberianDawn } } - private void SaveMapPreview(Stream stream, Boolean renderAll) + protected void SaveMapPreview(Stream stream, Boolean renderAll) { Map.GenerateMapPreview(renderAll ? this.GameType : GameType.None, renderAll).Save(stream); } - private void SaveJSON(JsonTextWriter writer) + protected void SaveJSON(JsonTextWriter writer) { writer.WriteStartObject(); writer.WritePropertyName("MapTileX"); @@ -2103,7 +2117,7 @@ namespace MobiusEditor.TiberianDawn return ok ? null : sb.ToString(); } - private void BasicSection_PropertyChanged(object sender, PropertyChangedEventArgs e) + protected void BasicSection_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { @@ -2116,7 +2130,7 @@ namespace MobiusEditor.TiberianDawn } } - private void MapSection_PropertyChanged(object sender, PropertyChangedEventArgs e) + protected void MapSection_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { @@ -2126,7 +2140,7 @@ namespace MobiusEditor.TiberianDawn } } - private void UpdateBasePlayerHouse() + protected void UpdateBasePlayerHouse() { Map.BasicSection.BasePlayer = HouseTypes.GetBasePlayer(Map.BasicSection.Player); var basePlayer = Map.HouseTypesIncludingNone.Where(h => h.Equals(Map.BasicSection.BasePlayer)).FirstOrDefault() ?? Map.HouseTypes.First(); @@ -2140,7 +2154,7 @@ namespace MobiusEditor.TiberianDawn } } - private void UpdateWaypoints() + protected void UpdateWaypoints() { bool isSolo = Map.BasicSection.SoloMission; for (int i = 0; i < multiStartPoints; ++i) @@ -2151,7 +2165,7 @@ namespace MobiusEditor.TiberianDawn } #region IDisposable Support - private bool disposedValue = false; + protected bool disposedValue = false; protected virtual void Dispose(bool disposing) { diff --git a/CnCTDRAMapEditor/TiberianDawn/GamePluginSS.cs b/CnCTDRAMapEditor/TiberianDawn/GamePluginSS.cs new file mode 100644 index 0000000..4ff1a19 --- /dev/null +++ b/CnCTDRAMapEditor/TiberianDawn/GamePluginSS.cs @@ -0,0 +1,97 @@ +using MobiusEditor.Interface; +using MobiusEditor.Model; +using MobiusEditor.Utility; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MobiusEditor.TiberianDawn +{ + public class GamePluginSS : GamePlugin + { + + protected const int cratePoints = 4; + protected const int teamStartPoints = 4; + + public GamePluginSS(IFeedBackHandler feedBackHandler) + : this(true, feedBackHandler) + { + } + + public static bool CheckForSSmap(String path, FileType fileType) + { + return CheckForMegamap(path, fileType) && CheckForIniInfo(path, fileType, "Crates", null, null); + } + + public GamePluginSS(Boolean mapImage, IFeedBackHandler feedBackHandler) + : base() + { + this.isMegaMap = true; + this.feedBackHandler = feedBackHandler; + var crateWaypoints = Enumerable.Range(0, cratePoints).Select(i => new Waypoint(string.Format("CR{0}", i), WaypointFlag.CrateSpawn)); + var teamWaypoints = Enumerable.Range(cratePoints, 25 - cratePoints).Select(i => new Waypoint(string.Format("TM{0}", i - cratePoints), WaypointFlag.PlayerStart)); + var generalWaypoints = Enumerable.Range(cratePoints + teamStartPoints, 25 - cratePoints - teamStartPoints).Select(i => new Waypoint(i.ToString())); + var specialWaypoints = new Waypoint[] { new Waypoint("Flare", WaypointFlag.Flare), new Waypoint("Home", WaypointFlag.Home), new Waypoint("Reinf.", WaypointFlag.Reinforce) }; + Waypoint[] waypoints = crateWaypoints.Concat(teamWaypoints).Concat(generalWaypoints).Concat(specialWaypoints).ToArray(); + var basicSection = new BasicSection(); + basicSection.SetDefault(); + var houseTypes = HouseTypesSS.GetTypes(); + basicSection.Player = HouseTypesSS.Admin.Name; + // Irrelevant for SS. Rebuilding options will be disabled in the editor. + basicSection.BasePlayer = HouseTypes.GetBasePlayer(basicSection.Player); + // I guess we leave these to the TD defaults. + string[] cellEventTypes = new[] + { + EventTypes.EVENT_PLAYER_ENTERED, + EventTypes.EVENT_NONE + }; + string[] unitEventTypes = + { + EventTypes.EVENT_DISCOVERED, + EventTypes.EVENT_ATTACKED, + EventTypes.EVENT_DESTROYED, + EventTypes.EVENT_ANY, + EventTypes.EVENT_NONE + }; + string[] structureEventTypes = (new[] { EventTypes.EVENT_PLAYER_ENTERED }).Concat(unitEventTypes).ToArray(); + string[] terrainEventTypes = + { + EventTypes.EVENT_ATTACKED, + EventTypes.EVENT_ANY, + EventTypes.EVENT_NONE + }; + string[] cellActionTypes = { }; + string[] unitActionTypes = { }; + string[] structureActionTypes = { }; + string[] terrainActionTypes = { }; + Map = new Map(basicSection, null, Constants.MaxSizeMega, typeof(House), + houseTypes, TheaterTypes.GetTypes(), TemplateTypes.GetTypes(), + TerrainTypes.GetTypes(), OverlayTypes.GetTypes(), SmudgeTypes.GetTypes(Globals.ConvertCraters), + EventTypes.GetTypes(), cellEventTypes, unitEventTypes, structureEventTypes, terrainEventTypes, + ActionTypes.GetTypes(), cellActionTypes, unitActionTypes, structureActionTypes, terrainActionTypes, + MissionTypes.GetTypes(), DirectionTypes.GetTypes(), InfantryTypes.GetTypes(), UnitTypes.GetTypes(Globals.DisableAirUnits), + BuildingTypes.GetTypes(), TeamMissionTypes.GetTypes(), fullTechnoTypes, waypoints, movieTypes, themeTypes) + { + TiberiumOrGoldValue = 25 + }; + Map.MapSection.PropertyChanged += MapSection_PropertyChanged; + // Clean up this mess. + foreach (House house in Map.Houses) + { + if (house.Type.ID > HouseTypesSS.Multi1.ID) + { + house.Enabled = false; + } + } + if (mapImage) + { + MapImage = new Bitmap(Map.Metrics.Width * Globals.MapTileWidth, Map.Metrics.Height * Globals.MapTileHeight); + } + } + + } +} diff --git a/CnCTDRAMapEditor/TiberianDawn/HouseTypes.cs b/CnCTDRAMapEditor/TiberianDawn/HouseTypes.cs index d5d4728..084a133 100644 --- a/CnCTDRAMapEditor/TiberianDawn/HouseTypes.cs +++ b/CnCTDRAMapEditor/TiberianDawn/HouseTypes.cs @@ -30,12 +30,12 @@ namespace MobiusEditor.TiberianDawn // Fixed to match actual game. Seems they messed up the naming of the colors in the xml files by taking the color definitions from the C&C // game code in order, arbitrarily naming those "Multi1" to "Multi6", and then correctly applying those obviously wrongly named colors to // the multi-Houses in the Remastered game. The editor code logically assumed they were named after their House, and thus got it all wrong. - public static readonly HouseType Multi1 = new HouseType(4, "Multi1", "MULTI2"); - public static readonly HouseType Multi2 = new HouseType(5, "Multi2", "MULTI5"); - public static readonly HouseType Multi3 = new HouseType(6, "Multi3", "MULTI4"); - public static readonly HouseType Multi4 = new HouseType(7, "Multi4", "MULTI6"); - public static readonly HouseType Multi5 = new HouseType(8, "Multi5", "MULTI1"); - public static readonly HouseType Multi6 = new HouseType(9, "Multi6", "MULTI3"); + public static readonly HouseType Multi1 = new HouseType(4, "Multi1", "MULTI2"); // Blue (originally teal) + public static readonly HouseType Multi2 = new HouseType(5, "Multi2", "MULTI5"); // Orange + public static readonly HouseType Multi3 = new HouseType(6, "Multi3", "MULTI4"); // Green + public static readonly HouseType Multi4 = new HouseType(7, "Multi4", "MULTI6"); // Teal (originally gray) + public static readonly HouseType Multi5 = new HouseType(8, "Multi5", "MULTI1"); // Yellow + public static readonly HouseType Multi6 = new HouseType(9, "Multi6", "MULTI3"); // Red private static readonly HouseType[] Types; diff --git a/CnCTDRAMapEditor/TiberianDawn/HouseTypesSS.cs b/CnCTDRAMapEditor/TiberianDawn/HouseTypesSS.cs new file mode 100644 index 0000000..b29d40d --- /dev/null +++ b/CnCTDRAMapEditor/TiberianDawn/HouseTypesSS.cs @@ -0,0 +1,145 @@ +using MobiusEditor.Model; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace MobiusEditor.TiberianDawn +{ + public static class HouseTypesSS + { + // Legacy + public static readonly HouseType Good = new HouseType(0, "GoodGuy", "GOOD"); + public static readonly HouseType Bad = new HouseType(1, "BadGuy", "BAD_UNIT", "BAD_STRUCTURE", ("harv", "BAD_STRUCTURE"), ("mcv", "BAD_STRUCTURE")); + public static readonly HouseType Neutral = new HouseType(2, "Neutral", "GOOD"); + public static readonly HouseType Special = new HouseType(3, "Special", "GOOD"); + // Special for SS + public static readonly HouseType Admin = new HouseType(4, "Admin", "NONE"); + public static readonly HouseType Spectator = new HouseType(5, "Spectator", "NONE"); + // Teams for football/CTF games + public static readonly HouseType Team1 = new HouseType(6, "Team 1", "MULTI2"); // Blue (originally teal) + public static readonly HouseType Team2 = new HouseType(7, "Team 2", "MULTI5"); // Orange + public static readonly HouseType Team3 = new HouseType(8, "Team 3", "MULTI4"); // Green + public static readonly HouseType Team4 = new HouseType(9, "Team 4", "MULTI6"); // Teal (originally gray) + // Guess I'll add these too? Kind of useless though... + public static readonly HouseType Multi1 = new HouseType(10, "Multi1", "GOOD"); + public static readonly HouseType Multi2 = new HouseType(11, "Multi2", "GOOD"); + public static readonly HouseType Multi3 = new HouseType(12, "Multi3", "GOOD"); + public static readonly HouseType Multi4 = new HouseType(13, "Multi4", "GOOD"); + public static readonly HouseType Multi5 = new HouseType(14, "Multi5", "GOOD"); + public static readonly HouseType Multi6 = new HouseType(15, "Multi6", "GOOD"); + public static readonly HouseType Multi7 = new HouseType(16, "Multi7", "GOOD"); + public static readonly HouseType Multi8 = new HouseType(17, "Multi8", "GOOD"); + public static readonly HouseType Multi9 = new HouseType(18, "Multi9", "GOOD"); + public static readonly HouseType Multi10 = new HouseType(19, "Multi10", "GOOD"); + public static readonly HouseType Multi11 = new HouseType(20, "Multi11", "GOOD"); + public static readonly HouseType Multi12 = new HouseType(21, "Multi12", "GOOD"); + public static readonly HouseType Multi13 = new HouseType(22, "Multi13", "GOOD"); + public static readonly HouseType Multi14 = new HouseType(23, "Multi14", "GOOD"); + public static readonly HouseType Multi15 = new HouseType(24, "Multi15", "GOOD"); + public static readonly HouseType Multi16 = new HouseType(25, "Multi16", "GOOD"); + public static readonly HouseType Multi17 = new HouseType(26, "Multi17", "GOOD"); + public static readonly HouseType Multi18 = new HouseType(27, "Multi18", "GOOD"); + public static readonly HouseType Multi19 = new HouseType(28, "Multi19", "GOOD"); + public static readonly HouseType Multi20 = new HouseType(29, "Multi20", "GOOD"); + public static readonly HouseType Multi21 = new HouseType(30, "Multi21", "GOOD"); + public static readonly HouseType Multi22 = new HouseType(31, "Multi22", "GOOD"); + public static readonly HouseType Multi23 = new HouseType(32, "Multi23", "GOOD"); + public static readonly HouseType Multi24 = new HouseType(33, "Multi24", "GOOD"); + public static readonly HouseType Multi25 = new HouseType(34, "Multi25", "GOOD"); + public static readonly HouseType Multi26 = new HouseType(35, "Multi26", "GOOD"); + public static readonly HouseType Multi27 = new HouseType(36, "Multi27", "GOOD"); + public static readonly HouseType Multi28 = new HouseType(37, "Multi28", "GOOD"); + public static readonly HouseType Multi29 = new HouseType(38, "Multi29", "GOOD"); + public static readonly HouseType Multi30 = new HouseType(39, "Multi30", "GOOD"); + public static readonly HouseType Multi31 = new HouseType(40, "Multi31", "GOOD"); + public static readonly HouseType Multi32 = new HouseType(41, "Multi32", "GOOD"); + public static readonly HouseType Multi33 = new HouseType(42, "Multi33", "GOOD"); + public static readonly HouseType Multi34 = new HouseType(43, "Multi34", "GOOD"); + public static readonly HouseType Multi35 = new HouseType(44, "Multi35", "GOOD"); + public static readonly HouseType Multi36 = new HouseType(45, "Multi36", "GOOD"); + public static readonly HouseType Multi37 = new HouseType(46, "Multi37", "GOOD"); + public static readonly HouseType Multi38 = new HouseType(47, "Multi38", "GOOD"); + public static readonly HouseType Multi39 = new HouseType(48, "Multi39", "GOOD"); + public static readonly HouseType Multi40 = new HouseType(49, "Multi40", "GOOD"); + public static readonly HouseType Multi41 = new HouseType(50, "Multi41", "GOOD"); + public static readonly HouseType Multi42 = new HouseType(51, "Multi42", "GOOD"); + public static readonly HouseType Multi43 = new HouseType(52, "Multi43", "GOOD"); + public static readonly HouseType Multi44 = new HouseType(53, "Multi44", "GOOD"); + public static readonly HouseType Multi45 = new HouseType(54, "Multi45", "GOOD"); + public static readonly HouseType Multi46 = new HouseType(55, "Multi46", "GOOD"); + public static readonly HouseType Multi47 = new HouseType(56, "Multi47", "GOOD"); + public static readonly HouseType Multi48 = new HouseType(57, "Multi48", "GOOD"); + public static readonly HouseType Multi49 = new HouseType(58, "Multi49", "GOOD"); + public static readonly HouseType Multi50 = new HouseType(59, "Multi50", "GOOD"); + public static readonly HouseType Multi51 = new HouseType(60, "Multi51", "GOOD"); + public static readonly HouseType Multi52 = new HouseType(61, "Multi52", "GOOD"); + public static readonly HouseType Multi53 = new HouseType(62, "Multi53", "GOOD"); + public static readonly HouseType Multi54 = new HouseType(63, "Multi54", "GOOD"); + public static readonly HouseType Multi55 = new HouseType(64, "Multi55", "GOOD"); + public static readonly HouseType Multi56 = new HouseType(65, "Multi56", "GOOD"); + public static readonly HouseType Multi57 = new HouseType(66, "Multi57", "GOOD"); + public static readonly HouseType Multi58 = new HouseType(67, "Multi58", "GOOD"); + public static readonly HouseType Multi59 = new HouseType(68, "Multi59", "GOOD"); + public static readonly HouseType Multi60 = new HouseType(69, "Multi60", "GOOD"); + public static readonly HouseType Multi61 = new HouseType(70, "Multi61", "GOOD"); + public static readonly HouseType Multi62 = new HouseType(71, "Multi62", "GOOD"); + public static readonly HouseType Multi63 = new HouseType(72, "Multi63", "GOOD"); + public static readonly HouseType Multi64 = new HouseType(73, "Multi64", "GOOD"); + public static readonly HouseType Multi65 = new HouseType(74, "Multi65", "GOOD"); + public static readonly HouseType Multi66 = new HouseType(75, "Multi66", "GOOD"); + public static readonly HouseType Multi67 = new HouseType(76, "Multi67", "GOOD"); + public static readonly HouseType Multi68 = new HouseType(77, "Multi68", "GOOD"); + public static readonly HouseType Multi69 = new HouseType(78, "Multi69", "GOOD"); + public static readonly HouseType Multi70 = new HouseType(79, "Multi70", "GOOD"); + public static readonly HouseType Multi71 = new HouseType(80, "Multi71", "GOOD"); + public static readonly HouseType Multi72 = new HouseType(81, "Multi72", "GOOD"); + public static readonly HouseType Multi73 = new HouseType(82, "Multi73", "GOOD"); + public static readonly HouseType Multi74 = new HouseType(83, "Multi74", "GOOD"); + public static readonly HouseType Multi75 = new HouseType(84, "Multi75", "GOOD"); + public static readonly HouseType Multi76 = new HouseType(85, "Multi76", "GOOD"); + public static readonly HouseType Multi77 = new HouseType(86, "Multi77", "GOOD"); + public static readonly HouseType Multi78 = new HouseType(87, "Multi78", "GOOD"); + public static readonly HouseType Multi79 = new HouseType(88, "Multi79", "GOOD"); + public static readonly HouseType Multi80 = new HouseType(89, "Multi80", "GOOD"); + public static readonly HouseType Multi81 = new HouseType(90, "Multi81", "GOOD"); + public static readonly HouseType Multi82 = new HouseType(91, "Multi82", "GOOD"); + public static readonly HouseType Multi83 = new HouseType(92, "Multi83", "GOOD"); + public static readonly HouseType Multi84 = new HouseType(93, "Multi84", "GOOD"); + public static readonly HouseType Multi85 = new HouseType(94, "Multi85", "GOOD"); + public static readonly HouseType Multi86 = new HouseType(95, "Multi86", "GOOD"); + public static readonly HouseType Multi87 = new HouseType(96, "Multi87", "GOOD"); + public static readonly HouseType Multi88 = new HouseType(97, "Multi88", "GOOD"); + public static readonly HouseType Multi89 = new HouseType(98, "Multi89", "GOOD"); + public static readonly HouseType Multi90 = new HouseType(99, "Multi90", "GOOD"); + public static readonly HouseType Multi91 = new HouseType(100, "Multi91", "GOOD"); + public static readonly HouseType Multi92 = new HouseType(101, "Multi92", "GOOD"); + public static readonly HouseType Multi93 = new HouseType(102, "Multi93", "GOOD"); + public static readonly HouseType Multi94 = new HouseType(103, "Multi94", "GOOD"); + public static readonly HouseType Multi95 = new HouseType(104, "Multi95", "GOOD"); + public static readonly HouseType Multi96 = new HouseType(105, "Multi96", "GOOD"); + public static readonly HouseType Multi97 = new HouseType(106, "Multi97", "GOOD"); + public static readonly HouseType Multi98 = new HouseType(107, "Multi98", "GOOD"); + public static readonly HouseType Multi99 = new HouseType(108, "Multi99", "GOOD"); + public static readonly HouseType Multi100 = new HouseType(109, "Multi100", "GOOD"); + + private static readonly HouseType[] Types; + + static HouseTypesSS() + { + Types = + (from field in typeof(HouseTypesSS).GetFields(BindingFlags.Static | BindingFlags.Public) + where field.IsInitOnly && typeof(HouseType).IsAssignableFrom(field.FieldType) + select field.GetValue(null) as HouseType).OrderBy(h => h.ID).ToArray(); + } + + public static IEnumerable GetTypes() + { + return Types; + } + + public static string GetBasePlayer(string player) + { + return Admin.Name; + } + } +} diff --git a/README.md b/README.md index e17fb66..14567cf 100644 --- a/README.md +++ b/README.md @@ -351,4 +351,4 @@ These options are all enabled by default, but can be disabled if you wish. Use t Some ideas that might get implemented in the future: -* Seems I'm all out! +* Add Sole Survivor as separate game type