diff --git a/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj b/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj
index 12a16a1..a1efe9d 100644
--- a/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj
+++ b/CnCTDRAMapEditor/CnCTDRAMapEditor.csproj
@@ -618,6 +618,8 @@
+
+
diff --git a/CnCTDRAMapEditor/Interface/IBrowsableType.cs b/CnCTDRAMapEditor/Interface/IBrowsableType.cs
index ce61bec..b5ee317 100644
--- a/CnCTDRAMapEditor/Interface/IBrowsableType.cs
+++ b/CnCTDRAMapEditor/Interface/IBrowsableType.cs
@@ -12,6 +12,8 @@
// distributed with this program. You should have received a copy of the
// 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 System;
using System.Drawing;
namespace MobiusEditor.Interface
@@ -20,5 +22,6 @@ namespace MobiusEditor.Interface
{
string DisplayName { get; }
Bitmap Thumbnail { get; }
+ void Reset();
}
}
diff --git a/CnCTDRAMapEditor/Interface/ITeamColor.cs b/CnCTDRAMapEditor/Interface/ITeamColor.cs
index 42442bb..e13e3b8 100644
--- a/CnCTDRAMapEditor/Interface/ITeamColor.cs
+++ b/CnCTDRAMapEditor/Interface/ITeamColor.cs
@@ -14,8 +14,6 @@ namespace MobiusEditor.Interface
{
/// Name of the color, used as identifier.
string Name { get; }
- /// A general color representing this team color.
- Color BaseColor { get; }
///
/// Apply this color to a given image.
diff --git a/CnCTDRAMapEditor/Interface/ITeamColorManager.cs b/CnCTDRAMapEditor/Interface/ITeamColorManager.cs
index a0b4c87..701f304 100644
--- a/CnCTDRAMapEditor/Interface/ITeamColorManager.cs
+++ b/CnCTDRAMapEditor/Interface/ITeamColorManager.cs
@@ -15,7 +15,12 @@ namespace MobiusEditor.Interface
Color RemapBaseColor { get; }
ITeamColor this[string key] { get; }
+ /// Gets a general color representing this team color.
+ /// Color key
+ /// The basic color for this team color.
+ Color GetBaseColor(string key);
+
void Load(string path);
- void Reset(GameType gameType);
+ void Reset(GameType gameType, string theater);
}
}
\ No newline at end of file
diff --git a/CnCTDRAMapEditor/MainForm.cs b/CnCTDRAMapEditor/MainForm.cs
index 75c6e37..eb64871 100644
--- a/CnCTDRAMapEditor/MainForm.cs
+++ b/CnCTDRAMapEditor/MainForm.cs
@@ -1059,7 +1059,7 @@ namespace MobiusEditor
{
var fileInfo = new FileInfo(fileName);
String name = fileInfo.FullName;
- if (!IdentifyMap(name, out FileType fileType, out GameType gameType, out bool isTdMegaMap))
+ if (!IdentifyMap(name, out FileType fileType, out GameType gameType, out bool isTdMegaMap, out string theater))
{
string extension = Path.GetExtension(name).TrimStart('.');
// No point in supporting jpeg here; the mapping needs distinct colours without fades.
@@ -1092,7 +1092,7 @@ namespace MobiusEditor
ModPaths.TryGetValue(gameType, out modPaths);
}
loadMultiThreader.ExecuteThreaded(
- () => LoadFile(name, fileType, gameType, isTdMegaMap, modPaths),
+ () => LoadFile(name, fileType, gameType, theater, isTdMegaMap, modPaths),
PostLoad, true,
(e,l) => LoadUnloadUi(e, l, loadMultiThreader),
"Loading map");
@@ -1138,10 +1138,11 @@ namespace MobiusEditor
"Saving map");
}
- private Boolean IdentifyMap(String loadFilename, out FileType fileType, out GameType gameType, out bool isTdMegaMap)
+ private Boolean IdentifyMap(String loadFilename, out FileType fileType, out GameType gameType, out bool isTdMegaMap, out string theater)
{
fileType = FileType.None;
gameType = GameType.None;
+ theater = null;
isTdMegaMap = false;
try
{
@@ -1212,6 +1213,7 @@ namespace MobiusEditor
{
return false;
}
+ theater = (iniContents["Map"].TryGetValue("Theater") ?? "temperate").ToLower();
switch (fileType)
{
case FileType.INI:
@@ -1308,20 +1310,20 @@ namespace MobiusEditor
}
}
- private static IGamePlugin LoadNewPlugin(GameType gameType, bool isTdMegaMap, string[] modPaths)
+ private static IGamePlugin LoadNewPlugin(GameType gameType, string theater, bool isTdMegaMap, string[] modPaths)
{
- return LoadNewPlugin(gameType, isTdMegaMap, modPaths, false);
+ return LoadNewPlugin(gameType, theater, isTdMegaMap, modPaths, false);
}
- private static IGamePlugin LoadNewPlugin(GameType gameType, bool isTdMegaMap, string[] modPaths, bool noImage)
+ private static IGamePlugin LoadNewPlugin(GameType gameType, string theater, bool isTdMegaMap, string[] modPaths, bool noImage)
{
// Resetting to a specific game type will take care of classic mode.
Globals.TheArchiveManager.ExpandModPaths = modPaths;
Globals.TheArchiveManager.Reset(gameType);
Globals.TheGameTextManager.Reset(gameType);
- Globals.TheTextureManager.Reset();
+ Globals.TheTextureManager.Reset(gameType, theater);
Globals.TheTilesetManager.Reset();
- Globals.TheTeamColorManager.Reset(gameType);
+ Globals.TheTeamColorManager.Reset(gameType, theater);
IGamePlugin plugin = null;
if (gameType == GameType.TiberianDawn)
{
@@ -1334,6 +1336,7 @@ namespace MobiusEditor
else if (gameType == GameType.RedAlert)
{
Globals.TheTeamColorManager.Load(@"DATA\XML\CNCRATEAMCOLORS.XML");
+ Globals.TheTeamColorManager.Load("palette.cps");
plugin = new RedAlert.GamePluginRA(!noImage);
}
else if (gameType == GameType.SoleSurvivor)
@@ -1358,35 +1361,26 @@ namespace MobiusEditor
new Vector3(0, 1, 1), new Vector2(0, 1), Color.FromArgb(61, 61, 59));
tcm.AddTeamColor(teamColorNone);
}
- else // if (teamColorManager is TeamRemapManager tcc)
- {
- // TODO classic team color
- //TeamRemap teamColorNone = new TeamRemap(tcc);
- //teamColorNone.Load(/*TODO*/);
- //tcc.AddTeamColor(teamColorNone);
- }
}
private static void AddTeamColorPurple(ITeamColorManager teamColorManager)
{
if (teamColorManager is TeamColorManager tcm)
{
- // Add extra purple for flag..
- TeamColor teamColorPurple = new TeamColor(tcm);
- teamColorPurple.Load("PURPLE", "BASE_TEAM",
+ // Add extra colors for flags.
+ TeamColor teamColorSeven = new TeamColor(tcm);
+ teamColorSeven.Load(tcm.GetItem("BAD_UNIT"), "MULTI7");
+ tcm.AddTeamColor(teamColorSeven);
+
+ TeamColor teamColorEight = new TeamColor(tcm);
+ teamColorEight.Load("MULTI8", "BASE_TEAM",
Color.FromArgb(66, 255, 0), Color.FromArgb(0, 255, 56), 0,
new Vector3(0.410f, 0.300f, 0.000f), new Vector3(0f, 1f, 1f), new Vector2(0.0f, 1.0f),
new Vector3(0, 1, 1), new Vector2(0, 1), Color.FromArgb(77, 13, 255));
- tcm.AddTeamColor(teamColorPurple);
- }
- else // if (teamColorManager is TeamRemapManager tcc)
- {
- // TODO classic team color
- //TeamRemap teamColorPurple = new TeamRemap(tcc);
- //teamColorPurple.Load(/*TODO*/);
- //tcc.AddTeamColor(teamColorPurple);
+ tcm.AddTeamColor(teamColorEight);
}
}
+
///
/// The separate-threaded part for making a new map.
///
@@ -1424,7 +1418,7 @@ namespace MobiusEditor
}
try
{
- IGamePlugin plugin = LoadNewPlugin(gameType, isTdMegaMap, modPaths);
+ IGamePlugin plugin = LoadNewPlugin(gameType, theater, isTdMegaMap, modPaths);
// This initialises the theater
plugin.New(theater);
if (SteamworksUGC.IsInit)
@@ -1488,11 +1482,11 @@ namespace MobiusEditor
///
///
///
- private static MapLoadInfo LoadFile(string loadFilename, FileType fileType, GameType gameType, bool isTdMegaMap, string[] modPaths)
+ private static MapLoadInfo LoadFile(string loadFilename, FileType fileType, GameType gameType, string theater, bool isTdMegaMap, string[] modPaths)
{
try
{
- IGamePlugin plugin = LoadNewPlugin(gameType, isTdMegaMap, modPaths);
+ IGamePlugin plugin = LoadNewPlugin(gameType, theater, isTdMegaMap, modPaths);
string[] errors = plugin.Load(loadFilename, fileType).ToArray();
return new MapLoadInfo(loadFilename, fileType, plugin, errors);
}
@@ -1650,7 +1644,7 @@ namespace MobiusEditor
}
// Unload graphics
Globals.TheTilesetManager.Reset();
- Globals.TheTextureManager.Reset();
+ Globals.TheTextureManager.Reset(GameType.None, null);
// Clean up loaded file status
filename = null;
loadedFileType = FileType.None;
diff --git a/CnCTDRAMapEditor/Model/BuildingType.cs b/CnCTDRAMapEditor/Model/BuildingType.cs
index 5796b2f..14bc349 100644
--- a/CnCTDRAMapEditor/Model/BuildingType.cs
+++ b/CnCTDRAMapEditor/Model/BuildingType.cs
@@ -64,7 +64,7 @@ namespace MobiusEditor.Model
public int Storage { get; set; }
- public Rectangle OverlapBounds => new Rectangle(Point.Empty, new Size(OccupyMask.GetLength(1), OccupyMask.GetLength(0)));
+ public Rectangle OverlapBounds => new Rectangle(Point.Empty, new Size(this.OccupyMask.GetLength(1), this.OccupyMask.GetLength(0)));
public bool[,] OpaqueMask { get; private set; }
public bool[,] OccupyMask { get; private set; }
@@ -76,19 +76,19 @@ namespace MobiusEditor.Model
public bool HasBib
{
- get { return (Flag & BuildingTypeFlag.Bib) == BuildingTypeFlag.Bib; }
+ get { return (this.Flag & BuildingTypeFlag.Bib) == BuildingTypeFlag.Bib; }
set
{
// Bibs are only supported for widths 2 to 4
- if (value && Size.Width >= 2 && Size.Width <= 4)
+ if (value && this.Size.Width >= 2 && this.Size.Width <= 4)
{
- Flag |= BuildingTypeFlag.Bib;
+ this.Flag |= BuildingTypeFlag.Bib;
}
else
{
- Flag &= ~BuildingTypeFlag.Bib;
+ this.Flag &= ~BuildingTypeFlag.Bib;
}
- RecalculateBibs();
+ this.RecalculateBibs();
}
}
@@ -101,14 +101,14 @@ namespace MobiusEditor.Model
public bool IsAircraft => false;
public bool IsFixedWing => false;
- public bool IsFake => (Flag & BuildingTypeFlag.Fake) == BuildingTypeFlag.Fake;
- public bool HasTurret => (Flag & BuildingTypeFlag.Turret) == BuildingTypeFlag.Turret;
- public bool IsSingleFrame => (Flag & BuildingTypeFlag.SingleFrame) == BuildingTypeFlag.SingleFrame;
- public bool CanRemap => (Flag & BuildingTypeFlag.NoRemap) != BuildingTypeFlag.NoRemap;
+ public bool IsFake => (this.Flag & BuildingTypeFlag.Fake) == BuildingTypeFlag.Fake;
+ public bool HasTurret => (this.Flag & BuildingTypeFlag.Turret) == BuildingTypeFlag.Turret;
+ public bool IsSingleFrame => (this.Flag & BuildingTypeFlag.SingleFrame) == BuildingTypeFlag.SingleFrame;
+ public bool CanRemap => (this.Flag & BuildingTypeFlag.NoRemap) != BuildingTypeFlag.NoRemap;
///
/// Indicates buildings that have pieces sticking out at the top that should not overlap the objects on these cells.
///
- public bool IsFlat => (Flag & BuildingTypeFlag.Flat) == BuildingTypeFlag.Flat;
+ public bool IsFlat => (this.Flag & BuildingTypeFlag.Flat) == BuildingTypeFlag.Flat;
public BuildingType(sbyte id, string name, string textId, int powerProd, int powerUse, int storage, int width, int height, string occupyMask, string ownerHouse, TheaterType[] theaters, string factoryOverlay, int frameOffset, String graphicsSource, BuildingTypeFlag flag)
{
@@ -118,7 +118,7 @@ namespace MobiusEditor.Model
this.Name = name;
this.GraphicsSource = graphicsSource ?? name;
this.DisplayName = !String.IsNullOrEmpty(textId) && !String.IsNullOrEmpty(Globals.TheGameTextManager[textId])
- ? Globals.TheGameTextManager[textId] + " (" + Name.ToUpperInvariant() + ")"
+ ? Globals.TheGameTextManager[textId] + " (" + this.Name.ToUpperInvariant() + ")"
: name.ToUpperInvariant();
this.PowerProduction = powerProd;
this.PowerUsage = powerUse;
@@ -184,30 +184,30 @@ namespace MobiusEditor.Model
private void RecalculateBibs()
{
- int maskY = BaseOccupyMask.GetLength(0);
- int maskX = BaseOccupyMask.GetLength(1);
- if (HasBib)
+ int maskY = this.BaseOccupyMask.GetLength(0);
+ int maskX = this.BaseOccupyMask.GetLength(1);
+ if (this.HasBib)
{
- OccupyMask = new bool[maskY + 1, maskX];
- for (var y = 0; y < maskY; ++y)
+ this.OccupyMask = new bool[maskY + 1, maskX];
+ for (int y = 0; y < maskY; ++y)
{
- for (var x = 0; x < maskX; ++x)
+ for (int x = 0; x < maskX; ++x)
{
- OccupyMask[y, x] = BaseOccupyMask[y, x];
+ this.OccupyMask[y, x] = this.BaseOccupyMask[y, x];
}
}
if (Globals.BlockingBibs)
{
- for (var x = 0; x < maskX; ++x)
+ for (int x = 0; x < maskX; ++x)
{
- OccupyMask[maskY, x] = true;
- OccupyMask[maskY - 1, x] = true;
+ this.OccupyMask[maskY, x] = true;
+ this.OccupyMask[maskY - 1, x] = true;
}
}
}
else
{
- OccupyMask = BaseOccupyMask;
+ this.OccupyMask = this.BaseOccupyMask;
}
}
@@ -218,15 +218,15 @@ namespace MobiusEditor.Model
int baseMaskX = this.BaseOccupyMask.GetLength(1);
string occupyMask = GeneralUtils.GetStringFromMask(this.BaseOccupyMask);
TheaterType[] theaters = null;
- if (Theaters != null)
+ if (this.Theaters != null)
{
- int thLen = Theaters.Length;
+ int thLen = this.Theaters.Length;
theaters = new TheaterType[thLen];
- Array.Copy(Theaters, theaters, thLen);
+ Array.Copy(this.Theaters, theaters, thLen);
}
// Don't do lookup of the UI name. We don't have the original lookup ID at this point, so don't bother, and just restore the name afterwards.
- BuildingType newBld = new BuildingType(ID, Name, null, PowerProduction, PowerUsage, Storage, baseMaskX, baseMaskY, occupyMask, OwnerHouse, theaters, FactoryOverlay, FrameOFfset, GraphicsSource, Flag);
- newBld.DisplayName = DisplayName;
+ BuildingType newBld = new BuildingType(this.ID, this.Name, null, this.PowerProduction, this.PowerUsage, this.Storage, baseMaskX, baseMaskY, occupyMask, this.OwnerHouse, theaters, this.FactoryOverlay, this.FrameOFfset, this.GraphicsSource, this.Flag);
+ newBld.DisplayName = this.DisplayName;
return newBld;
}
@@ -238,11 +238,11 @@ namespace MobiusEditor.Model
}
else if (obj is sbyte)
{
- return ID == (sbyte)obj;
+ return this.ID == (sbyte)obj;
}
else if (obj is string)
{
- return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
+ return string.Equals(this.Name, obj as string, StringComparison.OrdinalIgnoreCase);
}
return base.Equals(obj);
@@ -250,44 +250,44 @@ namespace MobiusEditor.Model
public override int GetHashCode()
{
- return ID.GetHashCode();
+ return this.ID.GetHashCode();
}
public override string ToString()
{
- return (Name ?? String.Empty).ToUpperInvariant();
+ return (this.Name ?? String.Empty).ToUpperInvariant();
}
public void Init(GameType gameType, TheaterType theater, HouseType house, DirectionType direction)
{
- var oldImage = Thumbnail;
- var mockBuilding = new Building()
+ Bitmap oldImage = this.Thumbnail;
+ Building mockBuilding = new Building()
{
Type = this,
House = house,
Strength = 256,
Direction = direction
};
- var render = MapRenderer.RenderBuilding(gameType, theater, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, mockBuilding);
+ (Rectangle, Action, bool) render = MapRenderer.RenderBuilding(gameType, theater, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, mockBuilding);
if (!render.Item1.IsEmpty)
{
- var th = new Bitmap(render.Item1.Width, render.Item1.Height);
+ Bitmap th = new Bitmap(render.Item1.Width, render.Item1.Height);
th.SetResolution(96, 96);
- using (var g = Graphics.FromImage(th))
+ using (Graphics g = Graphics.FromImage(th))
{
MapRenderer.SetRenderSettings(g, Globals.PreviewSmoothScale);
render.Item2(g);
- if (IsFake)
+ if (this.IsFake)
{
MapRenderer.RenderFakeBuildingLabel(g, mockBuilding, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, false);
}
}
- Thumbnail = th;
- OpaqueMask = GeneralUtils.FindOpaqueCells(th, Size, 10, 25, 0x80);
+ this.Thumbnail = th;
+ this.OpaqueMask = GeneralUtils.FindOpaqueCells(th, this.Size, 10, 25, 0x80);
}
else
{
- Thumbnail = null;
+ this.Thumbnail = null;
}
if (oldImage != null)
{
@@ -295,5 +295,16 @@ namespace MobiusEditor.Model
catch { /* ignore */ }
}
}
+
+ public void Reset()
+ {
+ Bitmap oldImage = this.Thumbnail;
+ this.Thumbnail = null;
+ if (oldImage != null)
+ {
+ try { oldImage.Dispose(); }
+ catch { /* ignore */ }
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/Model/InfantryType.cs b/CnCTDRAMapEditor/Model/InfantryType.cs
index 6b01f29..938de3a 100644
--- a/CnCTDRAMapEditor/Model/InfantryType.cs
+++ b/CnCTDRAMapEditor/Model/InfantryType.cs
@@ -122,5 +122,15 @@ namespace MobiusEditor.Model
catch { /* ignore */ }
}
}
+ public void Reset()
+ {
+ Bitmap oldImage = this.Thumbnail;
+ this.Thumbnail = null;
+ if (oldImage != null)
+ {
+ try { oldImage.Dispose(); }
+ catch { /* ignore */ }
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/Model/Map.cs b/CnCTDRAMapEditor/Model/Map.cs
index c2104be..5494ddf 100644
--- a/CnCTDRAMapEditor/Model/Map.cs
+++ b/CnCTDRAMapEditor/Model/Map.cs
@@ -1987,5 +1987,46 @@ namespace MobiusEditor.Model
}
return info;
}
+
+ public void ResetCachedGraphics()
+ {
+ // Dispose of cached images. This is non-destructive; the type objects themselves
+ // don't actually get disposed. Their thumbnail simply gets disposed and cleared.
+ foreach (ITechnoType technoType in this.AllTeamTechnoTypes)
+ {
+ // units, boats, aircraft, infantry
+ technoType.Reset();
+ }
+ // probably not needed since it's in the team techno types.
+ foreach (UnitType unitType in this.AllUnitTypes)
+ {
+ unitType.Reset();
+ }
+ // probably not needed since it's in the team techno types.
+ foreach (InfantryType infantryType in this.AllInfantryTypes)
+ {
+ infantryType.Reset();
+ }
+ foreach (BuildingType buildingType in this.BuildingTypes)
+ {
+ buildingType.Reset();
+ }
+ foreach (TemplateType template in this.TemplateTypes)
+ {
+ template.Reset();
+ }
+ foreach (TerrainType terrainType in this.TerrainTypes)
+ {
+ terrainType.Reset();
+ }
+ foreach (OverlayType overlayType in this.OverlayTypes)
+ {
+ overlayType.Reset();
+ }
+ foreach (SmudgeType smudgeType in this.SmudgeTypes)
+ {
+ smudgeType.Reset();
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/Model/OverlayType.cs b/CnCTDRAMapEditor/Model/OverlayType.cs
index 4329892..d978647 100644
--- a/CnCTDRAMapEditor/Model/OverlayType.cs
+++ b/CnCTDRAMapEditor/Model/OverlayType.cs
@@ -191,5 +191,15 @@ namespace MobiusEditor.Model
catch { /* ignore */ }
}
}
+ public void Reset()
+ {
+ Bitmap oldImage = this.Thumbnail;
+ this.Thumbnail = null;
+ if (oldImage != null)
+ {
+ try { oldImage.Dispose(); }
+ catch { /* ignore */ }
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/Model/SmudgeType.cs b/CnCTDRAMapEditor/Model/SmudgeType.cs
index 70005a7..121e2af 100644
--- a/CnCTDRAMapEditor/Model/SmudgeType.cs
+++ b/CnCTDRAMapEditor/Model/SmudgeType.cs
@@ -196,5 +196,15 @@ namespace MobiusEditor.Model
return null;
}
}
+ public void Reset()
+ {
+ Bitmap oldImage = this.Thumbnail;
+ this.Thumbnail = null;
+ if (oldImage != null)
+ {
+ try { oldImage.Dispose(); }
+ catch { /* ignore */ }
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/Model/TemplateType.cs b/CnCTDRAMapEditor/Model/TemplateType.cs
index daf45af..2fe4687 100644
--- a/CnCTDRAMapEditor/Model/TemplateType.cs
+++ b/CnCTDRAMapEditor/Model/TemplateType.cs
@@ -484,5 +484,15 @@ namespace MobiusEditor.Model
// Should never happen.
return new Point(0, 0);
}
+ public void Reset()
+ {
+ Bitmap oldImage = this.Thumbnail;
+ this.Thumbnail = null;
+ if (oldImage != null)
+ {
+ try { oldImage.Dispose(); }
+ catch { /* ignore */ }
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/Model/TerrainType.cs b/CnCTDRAMapEditor/Model/TerrainType.cs
index e2d9f1d..51d3805 100644
--- a/CnCTDRAMapEditor/Model/TerrainType.cs
+++ b/CnCTDRAMapEditor/Model/TerrainType.cs
@@ -219,5 +219,15 @@ namespace MobiusEditor.Model
catch { /* ignore */ }
}
}
+ public void Reset()
+ {
+ Bitmap oldImage = this.Thumbnail;
+ this.Thumbnail = null;
+ if (oldImage != null)
+ {
+ try { oldImage.Dispose(); }
+ catch { /* ignore */ }
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/Model/UnitType.cs b/CnCTDRAMapEditor/Model/UnitType.cs
index 4f11965..064dffb 100644
--- a/CnCTDRAMapEditor/Model/UnitType.cs
+++ b/CnCTDRAMapEditor/Model/UnitType.cs
@@ -184,5 +184,15 @@ namespace MobiusEditor.Model
catch { /* ignore */ }
}
}
+ public void Reset()
+ {
+ Bitmap oldImage = this.Thumbnail;
+ this.Thumbnail = null;
+ if (oldImage != null)
+ {
+ try { oldImage.Dispose(); }
+ catch { /* ignore */ }
+ }
+ }
}
}
diff --git a/CnCTDRAMapEditor/RedAlert/GamePluginRA.cs b/CnCTDRAMapEditor/RedAlert/GamePluginRA.cs
index f58d33d..29d7fd1 100644
--- a/CnCTDRAMapEditor/RedAlert/GamePluginRA.cs
+++ b/CnCTDRAMapEditor/RedAlert/GamePluginRA.cs
@@ -3803,7 +3803,10 @@ namespace MobiusEditor.RedAlert
{
if (disposing)
{
- MapImage?.Dispose();
+ try { MapImage?.Dispose(); }
+ catch { /* ignore */ }
+ // Dispose of cached images in type objects. This is non-destructive; the type objects themselves don't actually get disposed.
+ Map.ResetCachedGraphics();
}
disposedValue = true;
}
diff --git a/CnCTDRAMapEditor/Render/MapRenderer.cs b/CnCTDRAMapEditor/Render/MapRenderer.cs
index 098292e..f3f7352 100644
--- a/CnCTDRAMapEditor/Render/MapRenderer.cs
+++ b/CnCTDRAMapEditor/Render/MapRenderer.cs
@@ -1573,7 +1573,7 @@ namespace MobiusEditor.Render
return;
}
ITeamColor tc = Globals.TheTeamColorManager[building.House.BuildingTeamColor];
- Color circleColor = tc?.BaseColor ?? Globals.TheTeamColorManager.RemapBaseColor;
+ Color circleColor = Globals.TheTeamColorManager.GetBaseColor(tc.Name);
bool[,] cells = building.Type.BaseOccupyMask;
int maskY = cells.GetLength(0);
int maskX = cells.GetLength(1);
@@ -1601,7 +1601,7 @@ namespace MobiusEditor.Render
return;
}
ITeamColor tc = Globals.TheTeamColorManager[unit.House.BuildingTeamColor];
- Color circleColor = tc?.BaseColor ?? Globals.TheTeamColorManager.RemapBaseColor;
+ Color circleColor = Globals.TheTeamColorManager.GetBaseColor(tc.Name);
Color alphacorr = Color.FromArgb(unit.Tint.A * 128 / 256, circleColor);
if (isJammer)
{
diff --git a/CnCTDRAMapEditor/TiberianDawn/GamePluginTD.cs b/CnCTDRAMapEditor/TiberianDawn/GamePluginTD.cs
index 63cf448..f9c28af 100644
--- a/CnCTDRAMapEditor/TiberianDawn/GamePluginTD.cs
+++ b/CnCTDRAMapEditor/TiberianDawn/GamePluginTD.cs
@@ -394,9 +394,9 @@ namespace MobiusEditor.TiberianDawn
flagColors[mpId] = Globals.TheTeamColorManager[house.UnitTeamColor];
}
// Metallic light blue
- flagColors[6] = Globals.TheTeamColorManager["BAD_UNIT"];
+ flagColors[6] = Globals.TheTeamColorManager["MULTI7"];
// RA Purple
- flagColors[7] = Globals.TheTeamColorManager["PURPLE"];
+ flagColors[7] = Globals.TheTeamColorManager["MULTI8"];
Size mapSize = !megaMap ? Constants.MaxSize : Constants.MaxSizeMega;
Map = new Map(basicSection, null, mapSize, typeof(House), houseTypes,
flagColors, TheaterTypes.GetTypes(), TemplateTypes.GetTypes(),
@@ -3284,7 +3284,10 @@ namespace MobiusEditor.TiberianDawn
{
if (disposing)
{
- MapImage?.Dispose();
+ try { MapImage?.Dispose(); }
+ catch { /* ignore */ }
+ // Dispose of cached images in type objects. This is non-destructive; the type objects themselves don't actually get disposed.
+ Map.ResetCachedGraphics();
}
disposedValue = true;
}
diff --git a/CnCTDRAMapEditor/Utility/TeamColor.cs b/CnCTDRAMapEditor/Utility/TeamColor.cs
index 0f5c6d2..dd7367a 100644
--- a/CnCTDRAMapEditor/Utility/TeamColor.cs
+++ b/CnCTDRAMapEditor/Utility/TeamColor.cs
@@ -85,14 +85,13 @@ namespace MobiusEditor.Utility
public TeamColor(TeamColorManager teamColorManager, TeamColor col, string newName, Vector3 hsvShiftOverride)
{
this.teamColorManager = teamColorManager;
- this.Load(col);
- this.Name = newName;
+ this.Load(col, newName);
this.hsvShift = hsvShiftOverride;
}
- public void Load(TeamColor col)
+ public void Load(TeamColor col, string newName)
{
- this.Name = col.Name;
+ this.Name = newName ?? col.Name;
this.Variant = col.Variant;
this.lowerBounds = col.LowerBounds;
this.upperBounds = col.UpperBounds;
diff --git a/CnCTDRAMapEditor/Utility/TeamColorManager.cs b/CnCTDRAMapEditor/Utility/TeamColorManager.cs
index 290fc47..2d9d2e6 100644
--- a/CnCTDRAMapEditor/Utility/TeamColorManager.cs
+++ b/CnCTDRAMapEditor/Utility/TeamColorManager.cs
@@ -28,8 +28,16 @@ namespace MobiusEditor.Utility
private readonly IArchiveManager megafileManager;
public Color RemapBaseColor => Color.FromArgb(0x00, 0xFF, 0x00);
- public ITeamColor this[string key] => !string.IsNullOrEmpty(key) ? (ITeamColor)teamColors[key]: null;
+ public ITeamColor this[string key] => !string.IsNullOrEmpty(key) && teamColors.ContainsKey(key) ? (ITeamColor)teamColors[key]: null;
+ public Color GetBaseColor(string key)
+ {
+ if (!string.IsNullOrEmpty(key) && teamColors.ContainsKey(key))
+ {
+ return teamColors[key].BaseColor;
+ }
+ return RemapBaseColor;
+ }
public TeamColor GetItem(string key) => !string.IsNullOrEmpty(key) ? teamColors[key] : null;
public void RemoveTeamColor(string col)
@@ -56,12 +64,12 @@ namespace MobiusEditor.Utility
}
}
- public TeamColorManager(IArchiveManager megafileManager, params string[] expandModPaths)
+ public TeamColorManager(IArchiveManager megafileManager)
{
this.megafileManager = megafileManager;
}
- public void Reset(GameType gameType)
+ public void Reset(GameType gameType, string theater)
{
teamColors.Clear();
}
diff --git a/CnCTDRAMapEditor/Utility/TeamRemap.cs b/CnCTDRAMapEditor/Utility/TeamRemap.cs
new file mode 100644
index 0000000..f66ce04
--- /dev/null
+++ b/CnCTDRAMapEditor/Utility/TeamRemap.cs
@@ -0,0 +1,56 @@
+using MobiusEditor.Interface;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MobiusEditor.Utility
+{
+ class TeamRemap : ITeamColor
+ {
+ public string Name { get; private set; }
+ public byte UnitRadarColor { get; private set; }
+ public byte BuildingRadarColor { get; private set; }
+
+ private byte[] remapTable;
+
+ public TeamRemap(string name, byte unitRadarColor, byte buildingRadarColor, byte remapstart, byte[] remapValues)
+ {
+ this.Name = name;
+ this.UnitRadarColor = unitRadarColor;
+ this.BuildingRadarColor = buildingRadarColor;
+ this.remapTable = Enumerable.Range(0, 0x100).Cast().ToArray();
+ int max = Math.Max(0x100, remapstart + remapValues.Length);
+ for (int i = 0; i < max; ++i)
+ {
+ remapTable[remapstart + i] = remapValues[i];
+ }
+ }
+
+ public TeamRemap(string name, byte unitRadarColor, byte buildingRadarColor, byte[] remapOrigins, byte[] remapValues)
+ {
+ this.Name = name;
+ this.UnitRadarColor = unitRadarColor;
+ this.BuildingRadarColor = buildingRadarColor;
+ this.remapTable = Enumerable.Range(0, 0x100).Cast().ToArray();
+ int max = Math.Max(remapOrigins.Length, remapValues.Length);
+ for (int i = 0; i < max; ++i)
+ {
+ remapTable[remapOrigins[i]] = remapValues[i];
+ }
+ }
+
+
+ public void ApplyToImage(Bitmap image)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void ApplyToImage(Bitmap image, out Rectangle opaqueBounds)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/CnCTDRAMapEditor/Utility/TeamRemapManager.cs b/CnCTDRAMapEditor/Utility/TeamRemapManager.cs
new file mode 100644
index 0000000..d0eaab9
--- /dev/null
+++ b/CnCTDRAMapEditor/Utility/TeamRemapManager.cs
@@ -0,0 +1,137 @@
+using MobiusEditor.Interface;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MobiusEditor.Utility
+{
+ class TeamRemapManager : ITeamColorManager
+ {
+ // TD remap
+ public static readonly TeamRemap RemapTdGoodGuy = new TeamRemap("GOOD", 176, 180, 176, new byte[] { 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191 });
+ public static readonly TeamRemap RemapTdBadGuyRed = new TeamRemap("BAD_STRUCTURE", 127, 123, 176, new byte[] { 127, 126, 125, 124, 122, 46, 120, 47, 125, 124, 123, 122, 42, 121, 120, 120 });
+ public static readonly TeamRemap RemapTdBadGuyGray = new TeamRemap("BAD_UNIT", 127, 123, 176, new byte[] { 161, 200, 201, 202, 204, 205, 206, 12, 201, 202, 203, 204, 205, 115, 198, 114 });
+ // The color name identifiers in the remaster are all out of wack.
+ public static readonly TeamRemap RemapTdBlue = new TeamRemap("MULTI2", 2, 135, 176, new byte[] {2, 119, 118, 135, 136, 138, 112, 12, 118, 135, 136, 137, 138, 139, 114, 112});
+ public static readonly TeamRemap RemapTdOrange = new TeamRemap("MULTI5", 24, 26, 176, new byte[] { 24, 25, 26, 27, 29, 31, 46, 47, 26, 27, 28, 29, 30, 31, 43, 47 });
+ public static readonly TeamRemap RemapTdGreen = new TeamRemap("MULTI4", 167, 159, 176, new byte[] { 5, 165, 166, 167, 159, 142, 140, 199, 166, 167, 157, 3, 159, 143, 142, 141 });
+ public static readonly TeamRemap RemapTdLtBlue = new TeamRemap("MULTI6", 201, 203, 176, new byte[] { 161, 200, 201, 202, 204, 205, 206, 12, 201, 202, 203, 204, 205, 115, 198, 114 });
+ public static readonly TeamRemap RemapTdYellow = new TeamRemap("MULTI1", 5, 157, 176, new byte[] { 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191 });
+ public static readonly TeamRemap RemapTdRed = new TeamRemap("MULTI3", 127, 123, 176, new byte[] { 127, 126, 125, 124, 122, 46, 120, 47, 125, 124, 123, 122, 42, 121, 120, 120 });
+ // Added extra colours for flags
+ public static readonly TeamRemap RemapTdMul7 = new TeamRemap("MULTI7", 146, 209, 176, new byte[] { 146, 152, 209, 151, 173, 150, 173, 183, 146, 152, 209, 151, 173, 150, 173, 183 }); // Brown
+ public static readonly TeamRemap RemapTdMul8 = new TeamRemap("MULTI8", 214, 213, 176, new byte[] { 132, 133, 134, 213, 214, 121, 120, 12, 133, 134, 213, 214, 121, 174, 120, 199 }); // Burgundy
+ public static readonly TeamRemap RemapTdNone = new TeamRemap("NONE", 199, 199, 176, new byte[] { 14, 195, 196, 13, 169, 198, 199, 112, 14, 195, 196, 13, 169, 198, 199, 112 }); // Black
+
+
+ private static readonly Dictionary RemapsTd;
+
+ public const Byte MaxValueSixBit = 63;
+ private static readonly Byte[] ConvertToEightBit = new Byte[64];
+
+ static TeamRemapManager()
+ {
+ RemapsTd = (from field in typeof(ITeamColorManager).GetFields(BindingFlags.Static | BindingFlags.Public)
+ where field.IsInitOnly && typeof(TeamRemap).IsAssignableFrom(field.FieldType) && field.Name.StartsWith("RemapTd")
+ select field.GetValue(null) as TeamRemap).ToDictionary(trm => trm.Name);
+
+ // Build easy lookup tables for this, so no calculations are ever needed for this later.
+ for (Int32 i = 0; i < 64; ++i)
+ ConvertToEightBit[i] = (Byte)Math.Round(i * 255.0 / 63.0, MidpointRounding.ToEven);
+ }
+
+ private Dictionary remapsRa = new Dictionary();
+ private GameType currentlyLoadedGameType;
+ private Color[] currentlyLoadedPalette;
+ private readonly IArchiveManager mixfileManager;
+
+
+ public ITeamColor this[string key] => GetforCurrentGame(key);
+ public Color GetBaseColor(string key)
+ {
+ TeamRemap tc = GetforCurrentGame(key);
+ if (tc != null)
+ {
+ return currentlyLoadedPalette[tc.UnitRadarColor];
+ }
+ return RemapBaseColor;
+ }
+
+ private TeamRemap GetforCurrentGame(string key)
+ {
+ if (string.IsNullOrEmpty(key))
+ {
+ return null;
+ }
+ Dictionary currentRemaps;
+ switch (currentlyLoadedGameType)
+ {
+ case GameType.TiberianDawn:
+ case GameType.SoleSurvivor:
+ currentRemaps = RemapsTd;
+ break;
+ case GameType.RedAlert:
+ currentRemaps = this.remapsRa;
+ break;
+ default:
+ return null;
+ }
+ return currentRemaps.ContainsKey(key) ? currentRemaps[key] : null;
+ }
+
+ private Color remapBaseColor = Color.Black;
+ public Color RemapBaseColor => remapBaseColor;
+
+ public void Load(string path)
+ {
+ // Check if CPS, if so, fill in "this.remapsRa"
+ }
+
+ public TeamRemapManager(IArchiveManager fileManager)
+ {
+ this.mixfileManager = fileManager;
+ }
+
+ public void Reset(GameType gameType, string theater)
+ {
+ currentlyLoadedGameType = gameType;
+ currentlyLoadedPalette = new Color[0x100];
+ // file manager should already be reset to read from the correct game at this point.
+ using (Stream palette = mixfileManager.OpenFile(theater + ".pal"))
+ {
+ if (palette != null)
+ {
+ byte[] pal;
+ using (BinaryReader sr = new BinaryReader(palette))
+ {
+ pal = GeneralUtils.ReadAllBytes(sr);
+ }
+ int len = Math.Min(pal.Length / 3, 0x100);
+ int offs = 0;
+ for (int i = 0; i < len; ++i)
+ {
+ byte r = ConvertToEightBit[pal[offs + 0] & 0x3F];
+ byte g = ConvertToEightBit[pal[offs + 1] & 0x3F];
+ byte b = ConvertToEightBit[pal[offs + 2] & 0x3F];
+ currentlyLoadedPalette[i] = Color.FromArgb(r, g, b);
+ offs += 3;
+ }
+ }
+ }
+ if (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor)
+ {
+
+ }
+ }
+
+ public void setTheater()
+ {
+
+ }
+ }
+}
diff --git a/CnCTDRAMapEditor/Utility/TextureManager.cs b/CnCTDRAMapEditor/Utility/TextureManager.cs
index 64c1b66..03b1979 100644
--- a/CnCTDRAMapEditor/Utility/TextureManager.cs
+++ b/CnCTDRAMapEditor/Utility/TextureManager.cs
@@ -33,25 +33,16 @@ namespace MobiusEditor.Utility
private static string MissingTexture = "DATA\\ART\\TEXTURES\\SRGB\\COMMON\\MISC\\MISSING.TGA";
private bool processedMissingTexture = false;
-#if false
- private class ImageData
- {
- public TGA TGA;
- public JObject Metadata;
- }
-#endif
-
private readonly IArchiveManager megafileManager;
private Dictionary cachedTextures = new Dictionary();
- private Dictionary<(string, ITeamColor), (Bitmap, Rectangle)> teamColorTextures = new Dictionary<(string, ITeamColor), (Bitmap, Rectangle)>();
public TextureManager(IArchiveManager megafileManager)
{
this.megafileManager = megafileManager;
}
- public void Reset()
+ public void Reset(GameType gameType, string theater)
{
Bitmap[] cachedImages = cachedTextures.Values.ToArray();
cachedTextures.Clear();
@@ -67,20 +58,6 @@ namespace MobiusEditor.Utility
// Ignore.
}
}
- (Bitmap, Rectangle)[] cachedTeamImages = teamColorTextures.Values.ToArray();
- teamColorTextures.Clear();
- for (int i = 0; i < cachedTeamImages.Length; ++i)
- {
- try
- {
- (Bitmap bitmap, Rectangle opaqueBounds) = cachedTeamImages[i];
- bitmap.Dispose();
- }
- catch
- {
- // Ignore.
- }
- }
}
public (Bitmap, Rectangle) GetTexture(string filename, ITeamColor teamColor, bool generateFallback)
@@ -101,13 +78,9 @@ namespace MobiusEditor.Utility
return (retCopy, bounds);
}
}
- if (teamColorTextures.TryGetValue((filename, teamColor), out (Bitmap bitmap, Rectangle opaqueBounds) result))
- {
- Bitmap retCopy = new Bitmap(result.bitmap);
- retCopy.SetResolution(96, 96);
- return (retCopy, result.opaqueBounds);
- }
- if (!cachedTextures.TryGetValue(filename, out result.bitmap))
+ Bitmap resBitmap;
+ Rectangle resBounds = Rectangle.Empty;
+ if (!cachedTextures.TryGetValue(filename, out _))
{
if (Path.GetExtension(filename).ToLower() == ".tga")
{
@@ -181,71 +154,8 @@ namespace MobiusEditor.Utility
cachedTextures[filename] = bitmap;
}
}
-#if false
- // Attempt to load parent directory as archive
- var archiveDir = Path.GetDirectoryName(filename);
- var archivePath = archiveDir + ".ZIP";
- using (var fileStream = megafileManager.Open(archivePath))
- {
- if (fileStream != null)
- {
- using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Read))
- {
- var images = new Dictionary();
- foreach (var entry in archive.Entries)
- {
- var name = Path.GetFileNameWithoutExtension(entry.Name);
- if (!images.TryGetValue(name, out ImageData imageData))
- {
- imageData = images[name] = new ImageData { TGA = null, Metadata = null };
- }
- if ((imageData.TGA == null) && (Path.GetExtension(entry.Name).ToLower() == ".tga"))
- {
- using (var stream = entry.Open())
- using (var memStream = new MemoryStream())
- {
- stream.CopyTo(memStream);
- imageData.TGA = new TGA(memStream);
- }
- }
- else if ((imageData.Metadata == null) && (Path.GetExtension(entry.Name).ToLower() == ".meta"))
- {
- using (var stream = entry.Open())
- using (var reader = new StreamReader(stream))
- {
- imageData.Metadata = JObject.Parse(reader.ReadToEnd());
- }
- }
- if ((imageData.TGA != null) && (imageData.Metadata != null))
- {
- var bitmap = imageData.TGA.ToBitmap(true);
- var size = new Size(imageData.Metadata["size"][0].ToObject(), imageData.Metadata["size"][1].ToObject());
- var crop = Rectangle.FromLTRB(
- imageData.Metadata["crop"][0].ToObject(),
- imageData.Metadata["crop"][1].ToObject(),
- imageData.Metadata["crop"][2].ToObject(),
- imageData.Metadata["crop"][3].ToObject()
- );
- var uncroppedBitmap = new Bitmap(size.Width, size.Height, bitmap.PixelFormat);
- uncroppedBitmap.SetResolution(96, 96);
- using (var g = Graphics.FromImage(uncroppedBitmap))
- {
- g.DrawImage(bitmap, crop, new Rectangle(Point.Empty, bitmap.Size), GraphicsUnit.Pixel);
- }
- cachedTextures[Path.Combine(archiveDir, name) + ".tga"] = uncroppedBitmap;
- images.Remove(name);
- }
- }
- foreach (var item in images.Where(x => x.Value.TGA != null))
- {
- cachedTextures[Path.Combine(archiveDir, item.Key) + ".tga"] = item.Value.TGA.ToBitmap(true);
- }
- }
- }
- }
-#endif
}
- if (!cachedTextures.TryGetValue(filename, out result.bitmap))
+ if (!cachedTextures.TryGetValue(filename, out resBitmap))
{
// Try loading as a DDS
var ddsFilename = Path.ChangeExtension(filename, ".DDS");
@@ -260,26 +170,25 @@ namespace MobiusEditor.Utility
}
}
}
- if (!cachedTextures.TryGetValue(filename, out result.bitmap))
+ if (!cachedTextures.TryGetValue(filename, out resBitmap))
{
- return result;
+ return (resBitmap, resBounds);
}
- Bitmap resBm = new Bitmap(result.bitmap);
+ // Clone returned image.
+ Bitmap resBm = new Bitmap(resBitmap);
resBm.SetResolution(96, 96);
- result.bitmap = resBm;
+ resBitmap = resBm;
if (teamColor != null)
{
Rectangle opaqueBounds;
teamColor.ApplyToImage(resBm, out opaqueBounds);
- result.opaqueBounds = opaqueBounds;
- // EXPERIMENTAL: might be better not to cache this?
- //teamColorTextures[(filename, teamColor)] = (new Bitmap(result.bitmap), result.opaqueBounds);
+ resBounds = opaqueBounds;
}
else
{
- result.opaqueBounds = ImageUtils.CalculateOpaqueBounds(resBm);
+ resBounds = ImageUtils.CalculateOpaqueBounds(resBm);
}
- return result;
+ return (resBitmap, resBounds);
}
private void LoadTgaFromZipFileStream(Stream fileStream, String name, ref TGA tga, ref JObject metadata)
diff --git a/CnCTDRAMapEditor/Utility/Tileset.cs b/CnCTDRAMapEditor/Utility/Tileset.cs
index 7acba72..430e104 100644
--- a/CnCTDRAMapEditor/Utility/Tileset.cs
+++ b/CnCTDRAMapEditor/Utility/Tileset.cs
@@ -23,7 +23,7 @@ using System.Xml;
namespace MobiusEditor.Utility
{
- public class Tile
+ public class Tile: IDisposable
{
public Bitmap Image { get; private set; }
@@ -39,6 +39,24 @@ namespace MobiusEditor.Utility
: this(image, new Rectangle(0, 0, image.Width, image.Height))
{
}
+
+ public void Dispose()
+ {
+ Bitmap image = this.Image;
+ this.Image = null;
+ try
+ {
+ if (image != null)
+ {
+ image.Dispose();
+ }
+ }
+ catch
+ {
+ // ignore.
+ }
+
+ }
}
public class Tileset
@@ -77,6 +95,17 @@ namespace MobiusEditor.Utility
{
foreach (var tileItem in item.Value)
{
+ // no need to dispose the images; the Reset of TextureManager handles that.
+ foreach (KeyValuePair tileinfo in tileItem.Value.TeamColorTiles)
+ {
+ if (tileinfo.Value != null) {
+ foreach (Tile tile in tileinfo.Value)
+ {
+ // clean up bitmap
+ tile.Dispose();
+ }
+ }
+ }
tileItem.Value.TeamColorTiles.Clear();
}
}