Finished classic graphics mode (besides RA mix encryption).

* Added RA template reading.
* Added support for RA infantry remapping.
* Fixed bugs in text indicator scaling.
* Added separate scaling factors for classic mode.
* Added automatic extra scaling to tool list preview graphics to compensate for the smaller classic graphics.
* Fixed swapped power usage and production of RA Gap Generator.
* Set correct preview House for the Ants and Queen Ant (and larvae).
* Fixed logic to load mix files from inside other mix files.
* Fixed logic to only apply remap to a specific rectangle inside an image.
* Fixed logic to load TD remap schemes.
* Added logic to give classic dummy image semitransparent edges.
* Fixed logic to check the amount of frames of a sprite in classic mode without side effects.
* Fixed bug where the Template tool and the "new from image" dialog used a tile size derived from the map scale factor instead of the preview scale factor to determine in which cell the user clicks.
This commit is contained in:
Nyerguds 2023-06-16 21:46:44 +02:00
parent b611342c18
commit 3f673f8ace
38 changed files with 710 additions and 285 deletions

View File

@ -393,6 +393,7 @@
<Compile Include="RedAlert\GamePluginRA.cs" />
<Compile Include="RedAlert\House.cs" />
<Compile Include="RedAlert\HouseTypes.cs" />
<Compile Include="RedAlert\InfantryClassicRemap.cs" />
<Compile Include="RedAlert\InfantryTypes.cs" />
<Compile Include="RedAlert\MissionTypes.cs" />
<Compile Include="RedAlert\OverlayTypes.cs" />

View File

@ -405,6 +405,7 @@ namespace MobiusEditor.Controls
PreRender?.Invoke(this, new RenderEventArgs(pe.Graphics, fullInvalidation ? null : invalidateCells));
Image mapImg = mapImage;
MapRenderer.SetRenderSettings(pe.Graphics, SmoothScale);
if (mapImg != null)
{
pe.Graphics.Transform = compositeTransform;
@ -412,7 +413,6 @@ namespace MobiusEditor.Controls
var oldCompositingQuality = pe.Graphics.CompositingQuality;
var oldInterpolationMode = pe.Graphics.InterpolationMode;
var oldPixelOffsetMode = pe.Graphics.PixelOffsetMode;
MapRenderer.SetRenderSettings(pe.Graphics, SmoothScale);
pe.Graphics.DrawImage(mapImg, 0, 0);
pe.Graphics.CompositingMode = oldCompositingMode;
pe.Graphics.CompositingQuality = oldCompositingQuality;

View File

@ -360,7 +360,7 @@ namespace MobiusEditor.Dialogs
}
templateTypeMapPanel.MapImage = selected.Thumbnail;
var templateTypeMetrics = new CellMetrics(selected.ThumbnailIconWidth, selected.ThumbnailIconHeight);
templateTypeNavigationWidget = new NavigationWidget(templateTypeMapPanel, templateTypeMetrics, Globals.OriginalTileSize, false);
templateTypeNavigationWidget = new NavigationWidget(templateTypeMapPanel, templateTypeMetrics, Globals.PreviewTileSize, false);
templateTypeNavigationWidget.MouseoverSize = Size.Empty;
templateTypeNavigationWidget.Activate();
}

View File

@ -24,8 +24,6 @@ namespace MobiusEditor
{
static Globals()
{
SetTileSize(false);
double minScale = 1.0 / Math.Min(OriginalTileWidth, OriginalTileHeight);
// Startup options
UseClassicGraphics = Properties.Settings.Default.UseClassicGraphics;
// Defaults
@ -36,16 +34,10 @@ namespace MobiusEditor
OutlineAllCrates = Properties.Settings.Default.DefaultOutlineAllCrates;
CratesOnTop = Properties.Settings.Default.DefaultCratesOnTop;
ShowMapGrid = Properties.Settings.Default.DefaultShowMapGrid;
ExportTileScale = Math.Min(1, Math.Max(minScale, Math.Abs(Properties.Settings.Default.DefaultExportScale)));
ExportSmoothScale = Properties.Settings.Default.DefaultExportScale < 0;
// Fine tuning
ZoomToBoundsOnLoad = Properties.Settings.Default.ZoomToBoundsOnLoad;
MapGridColor = Properties.Settings.Default.MapGridColor;
MapBackColor = Color.FromArgb(255, Properties.Settings.Default.MapBackColor);
MapTileScale = Math.Min(1, Math.Max(minScale, Math.Abs(Properties.Settings.Default.MapScale)));
MapSmoothScale = Properties.Settings.Default.MapScale < 0;
PreviewTileScale = Math.Min(1, Math.Max(minScale, Math.Abs(Properties.Settings.Default.PreviewScale)));
PreviewSmoothScale = Properties.Settings.Default.PreviewScale < 0;
UndoRedoStackSize = Properties.Settings.Default.UndoRedoStackSize;
MinimumClampSize = Properties.Settings.Default.MinimumClampSize;
// Behavior tweaks
@ -69,14 +61,8 @@ namespace MobiusEditor
public const string MegafilePath = @"DATA";
public const string GameTextFilenameFormat = @"DATA\TEXT\MASTERTEXTFILE_{0}.LOC";
public static void SetTileSize(bool classic)
{
OriginalTileWidth = classic ? 24 : 128;
OriginalTileHeight = classic ? 24 : 128;
}
public static int OriginalTileWidth { get; private set; }
public static int OriginalTileHeight { get; private set; }
public static int OriginalTileWidth { get { return UseClassicGraphics ? 24 : 128; } }
public static int OriginalTileHeight { get { return UseClassicGraphics ? 24 : 128; } }
public static Size OriginalTileSize => new Size(OriginalTileWidth, OriginalTileHeight);
public const int PixelWidth = 24;
@ -92,21 +78,50 @@ namespace MobiusEditor
public static bool CratesOnTop { get; set; }
public static bool OutlineAllCrates { get; set; }
public static bool ShowMapGrid { get; set; }
public static double ExportTileScale { get; private set; }
public static bool ExportSmoothScale { get; private set; }
public static double ExportTileScale
{
get
{
double defExpScale = UseClassicGraphics ? Properties.Settings.Default.DefaultExportScaleClassic : Properties.Settings.Default.DefaultExportScale;
return Math.Max(GetMinScale(), Math.Abs(defExpScale));
}
}
public static bool ExportSmoothScale
{
get
{
return (UseClassicGraphics ? Properties.Settings.Default.DefaultExportScaleClassic : Properties.Settings.Default.DefaultExportScale) < 0;
}
}
public static bool ZoomToBoundsOnLoad { get; private set; }
public static Color MapGridColor { get; private set; }
public static Color MapBackColor { get; private set; }
public static double MapTileScale { get; private set; }
public static bool MapSmoothScale { get; private set; }
private static double GetMinScale(){ return 1.0 / Math.Min(OriginalTileWidth, OriginalTileHeight); }
public static double MapTileScale => Math.Max(GetMinScale(), Math.Abs(UseClassicGraphics ? Properties.Settings.Default.MapScaleClassic : Properties.Settings.Default.MapScale));
public static bool MapSmoothScale => (UseClassicGraphics ? Properties.Settings.Default.MapScaleClassic : Properties.Settings.Default.MapScale) < 0;
public static int MapTileWidth => Math.Max(1, (int)(OriginalTileWidth * MapTileScale));
public static int MapTileHeight => Math.Max(1, (int)(OriginalTileHeight * MapTileScale));
public static Size MapTileSize => new Size(MapTileWidth, MapTileHeight);
public static double PreviewTileScale { get; private set; }
public static bool PreviewSmoothScale { get; private set; }
public static double PreviewTileScale
{
get
{
double prevTileScale = Math.Max(GetMinScale(), Math.Abs(Properties.Settings.Default.PreviewScale));
if (UseClassicGraphics)
{
// Adjust to classic graphics' considerably smaller overall size
prevTileScale = prevTileScale * 128 / 24;
}
return prevTileScale;
}
}
public static bool PreviewSmoothScale => Properties.Settings.Default.PreviewScale < 0;
public static int PreviewTileWidth => Math.Max(1, (int)(OriginalTileWidth * PreviewTileScale));
public static int PreviewTileHeight => (int)(OriginalTileHeight * PreviewTileScale);
public static Size PreviewTileSize => new Size(PreviewTileWidth, PreviewTileHeight);

View File

@ -28,5 +28,7 @@ namespace MobiusEditor.Interface
bool IsFixedWing { get; }
/// <summary>True if this object can harvest resources. This affects the default orders for placing it on the map.</summary>
bool IsHarvester { get; }
/// <summary>True if this techno type adapts to its house colors.</summary>
bool CanRemap { get; }
}
}

View File

@ -2435,9 +2435,14 @@ namespace MobiusEditor
if (!gotBeacon)
{
// Beacon only exists in rematered graphics. Get fallback.
Globals.TheTilesetManager.GetTileData("armor", 6, out waypoint);
int icn = plugin.GameType == GameType.RedAlert ? 15 : 12;
Globals.TheTilesetManager.GetTileData("mouse", icn, out waypoint);
}
Globals.TheTilesetManager.GetTileData("mine.shp", 3, out Tile cellTrigger);
if (cellTrigger == null)
{
Globals.TheTilesetManager.GetTileData("mine", 3, out cellTrigger);
}
Globals.TheTilesetManager.GetTileData("mine", 3, out Tile cellTrigger);
LoadNewIcon(mapToolStripButton, templateTile?.Image, plugin, 0);
LoadNewIcon(smudgeToolStripButton, smudge?.Thumbnail, plugin, 1);
//LoadNewIcon(overlayToolStripButton, overlayTile?.Image, plugin, 2);

View File

@ -267,7 +267,7 @@ namespace MobiusEditor.Model
render.Item2(g);
if (this.IsFake)
{
MapRenderer.RenderFakeBuildingLabel(g, mockBuilding, Point.Empty, Globals.PreviewTileSize, Globals.PreviewTileScale, false);
MapRenderer.RenderFakeBuildingLabel(g, mockBuilding, Point.Empty, Globals.PreviewTileSize, false);
}
}
this.Thumbnail = th;

View File

@ -32,6 +32,10 @@ namespace MobiusEditor.Model
public bool IsFixedWing => false;
public bool IsExpansionUnit => (Flag & UnitTypeFlag.IsExpansionUnit) == UnitTypeFlag.IsExpansionUnit;
public bool IsHarvester => false;
public bool CanRemap => (this.Flag & UnitTypeFlag.NoRemap) != UnitTypeFlag.NoRemap;
public string ClassicGraphicsSource { get; private set; }
public Byte[] ClassicGraphicsRemap { get; private set; }
private Size _RenderSize;
public Size GetRenderSize(Size cellSize)
{
@ -41,17 +45,23 @@ namespace MobiusEditor.Model
public Bitmap Thumbnail { get; set; }
private string nameId;
public InfantryType(sbyte id, string name, string textId, string ownerHouse, UnitTypeFlag flags)
public InfantryType(sbyte id, string name, string textId, string ownerHouse, string remappedFrom, byte[] remapTable, UnitTypeFlag flags)
{
this.ID = id;
this.Name = name;
this.nameId = textId;
this.OwnerHouse = ownerHouse;
this.Flag = flags;
this.ClassicGraphicsSource = remappedFrom;
this.ClassicGraphicsRemap = remapTable;
}
public InfantryType(sbyte id, string name, string textId, string ownerHouse, UnitTypeFlag flags)
: this(id, name, textId, ownerHouse, null, null, flags)
{
}
public InfantryType(sbyte id, string name, string textId, string ownerHouse)
: this(id, name, textId, ownerHouse, UnitTypeFlag.None)
: this(id, name, textId, ownerHouse, null, null, UnitTypeFlag.None)
{
}
@ -89,7 +99,17 @@ namespace MobiusEditor.Model
? Globals.TheGameTextManager[nameId] + " (" + Name.ToUpperInvariant() + ")"
: Name.ToUpperInvariant();
Bitmap oldImage = Thumbnail;
if (Globals.TheTilesetManager.GetTileData(Name, 4, out Tile tile))
Tile tile;
// RA classic infantry remap support.
if (this.ClassicGraphicsSource != null && Globals.TheTilesetManager is TilesetManagerClassic tsmc)
{
tsmc.GetTeamColorTileData(this.Name, 4, null, out tile, true, false, this.ClassicGraphicsSource, this.ClassicGraphicsRemap);
}
else
{
Globals.TheTilesetManager.GetTeamColorTileData(this.Name, 4, null, out tile, true, false);
}
if (tile != null && tile.Image != null)
{
_RenderSize = tile.Image.Size;
}

View File

@ -1412,7 +1412,7 @@ namespace MobiusEditor.Model
}
using (Graphics g = Graphics.FromImage(croppedBitmap))
{
MapRenderer.SetRenderSettings(g, true);
MapRenderer.SetRenderSettings(g, smooth);
Matrix transform = new Matrix();
transform.Scale(previewScale, previewScale);
transform.Translate((scaledSize.Width - mapBounds.Width) / 2, (scaledSize.Height - mapBounds.Height) / 2);

View File

@ -37,6 +37,7 @@ namespace MobiusEditor.Model
public bool IsAircraft => false;
public bool IsFixedWing => false;
public bool IsHarvester => false;
public bool CanRemap => false;
private string nameId;
public Size GetRenderSize(Size cellSize)
@ -186,7 +187,14 @@ namespace MobiusEditor.Model
: Name.ToUpperInvariant();
var oldImage = Thumbnail;
string tileName = GraphicsSource;
if (Globals.TheTilesetManager.GetTileData(tileName, DisplayIcon, out Tile tile))
Tile tile;
// If the graphics source doesn't yield a result, fall back on actual name.
// This fixes the graphics source override for the ore mine not working in classic mode.
if (!Globals.TheTilesetManager.GetTileData(tileName, DisplayIcon, out tile))
{
Globals.TheTilesetManager.GetTileData(this.Name, DisplayIcon, out tile);
}
if (tile != null)
{
var tileSize = Globals.PreviewTileSize;
var renderSize = new Size(tileSize.Width * Size.Width, tileSize.Height * Size.Height);

View File

@ -35,12 +35,14 @@ namespace MobiusEditor.Model
IsArmed = 1 << 3,
/// <summary>Can harvest resources. This affects the default orders for placing it on the map.</summary>
IsHarvester = 1 << 4,
/// <summary>Can attack units. This affects the default orders for placing it on the map.</summary>
NoRemap = 1 << 5,
/// <summary>Is a unit that is filtered out of the lists if expansion units are disabled.</summary>
IsExpansionUnit = 1 << 5,
IsExpansionUnit = 1 << 6,
/// <summary>Can show a mobile gap area-of-effect radius indicator.</summary>
IsGapGenerator = 1 << 6,
IsGapGenerator = 1 << 7,
/// <summary>Can show a radar jamming area-of-effect radius indicator.</summary>
IsJammer = 1 << 7,
IsJammer = 1 << 8,
}
public static class UnitTypeIDMask
@ -72,6 +74,7 @@ namespace MobiusEditor.Model
public bool IsArmed => (this.Flag & UnitTypeFlag.IsArmed) == UnitTypeFlag.IsArmed;
public bool IsHarvester => (this.Flag & UnitTypeFlag.IsHarvester) == UnitTypeFlag.IsHarvester;
public bool IsExpansionUnit => (this.Flag & UnitTypeFlag.IsExpansionUnit) == UnitTypeFlag.IsExpansionUnit;
public bool CanRemap => (this.Flag & UnitTypeFlag.NoRemap) != UnitTypeFlag.NoRemap;
private Size _RenderSize;
private string nameId;

View File

@ -13,7 +13,7 @@
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
//#define CLASSICIMPLEMENTED
#define CLASSICIMPLEMENTED
using MobiusEditor.Dialogs;
using MobiusEditor.Interface;
@ -133,7 +133,6 @@ namespace MobiusEditor
private static void LoadEditorClassic()
{
Globals.SetTileSize(true);
// The system should scan all mix archives for known filenames of other mix archives so it can do recursive searches.
// Mix files should be given in order or depth, so first give ones that are in the folder, then ones that may occur inside others.
// The order of load determines the file priority; only the first found occurrence of a file is used.

View File

@ -12,7 +12,7 @@ namespace MobiusEditor.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.2.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -131,6 +131,15 @@ namespace MobiusEditor.Properties {
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1.0")]
public double DefaultExportScaleClassic {
get {
return ((double)(this["DefaultExportScaleClassic"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
@ -167,6 +176,15 @@ namespace MobiusEditor.Properties {
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1.0")]
public double MapScaleClassic {
get {
return ((double)(this["MapScaleClassic"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1")]

View File

@ -38,6 +38,9 @@
<Setting Name="DefaultExportScale" Type="System.Double" Scope="Application">
<Value Profile="(Default)">-0.5</Value>
</Setting>
<Setting Name="DefaultExportScaleClassic" Type="System.Double" Scope="Application">
<Value Profile="(Default)">1.0</Value>
</Setting>
<Setting Name="ZoomToBoundsOnLoad" Type="System.Boolean" Scope="Application">
<Value Profile="(Default)">True</Value>
</Setting>
@ -50,6 +53,9 @@
<Setting Name="MapScale" Type="System.Double" Scope="Application">
<Value Profile="(Default)">0.5</Value>
</Setting>
<Setting Name="MapScaleClassic" Type="System.Double" Scope="Application">
<Value Profile="(Default)">1.0</Value>
</Setting>
<Setting Name="PreviewScale" Type="System.Double" Scope="Application">
<Value Profile="(Default)">1</Value>
</Setting>

View File

@ -30,7 +30,7 @@ namespace MobiusEditor.RedAlert
public static readonly BuildingType Pillbox = new BuildingType(4, "pbox", "TEXT_STRUCTURE_RA_PBOX", 0, 15, 1, 1, null, "Greece");
public static readonly BuildingType CamoPillbox = new BuildingType(5, "hbox", "TEXT_STRUCTURE_RA_HBOX", 0, 15, 1, 1, null, "Greece");
public static readonly BuildingType Command = new BuildingType(6, "dome", "TEXT_STRUCTURE_RA_DOME", 0, 40, 2, 2, null, "Greece", BuildingTypeFlag.Bib);
public static readonly BuildingType GapGenerator = new BuildingType(7, "gap", "TEXT_STRUCTURE_RA_GAP", 60, 0, 1, 2, "0 1", "Greece", 13, BuildingTypeFlag.IsGapGenerator);
public static readonly BuildingType GapGenerator = new BuildingType(7, "gap", "TEXT_STRUCTURE_RA_GAP", 0, 60, 1, 2, "0 1", "Greece", 13, BuildingTypeFlag.IsGapGenerator);
public static readonly BuildingType Turret = new BuildingType(8, "gun", "TEXT_STRUCTURE_RA_GUN", 0, 40, 1, 1, null, "Greece", BuildingTypeFlag.Turret);
public static readonly BuildingType AAGun = new BuildingType(9, "agun", "TEXT_STRUCTURE_RA_AGUN", 0, 50, 1, 2, "0 1", "Greece", BuildingTypeFlag.Turret);
public static readonly BuildingType FlameTurret = new BuildingType(10, "ftur", "TEXT_STRUCTURE_RA_FTUR", 0, 20, 1, 1, null, "USSR");
@ -52,7 +52,7 @@ namespace MobiusEditor.RedAlert
public static readonly BuildingType Mission = new BuildingType(26, "miss", "TEXT_STRUCTURE_RA_MISS", 0, 0, 3, 2, null, "Greece", BuildingTypeFlag.Bib);
public static readonly BuildingType ShipYard = new BuildingType(27, "syrd", "TEXT_STRUCTURE_RA_SYRD", 0, 30, 3, 3, null, "Greece");
public static readonly BuildingType SubPen = new BuildingType(28, "spen", "TEXT_STRUCTURE_RA_SPEN", 0, 30, 3, 3, null, "USSR");
public static readonly BuildingType MissileSilo = new BuildingType(29, "mslo", "TEXT_STRUCTURE_RA_MSLO", 0, 100, 2, 1, null, "Greece", new[] { TheaterTypes.Temperate, TheaterTypes.Snow });
public static readonly BuildingType MissileSilo = new BuildingType(29, "mslo", "TEXT_STRUCTURE_RA_MSLO", 0, 100, 2, 1, null, "Greece");
public static readonly BuildingType ForwardCom = new BuildingType(30, "fcom", "TEXT_STRUCTURE_RA_FCOM", 0, 200, 2, 2, "00 11", "USSR", BuildingTypeFlag.Bib);
public static readonly BuildingType Tesla = new BuildingType(31, "tsla", "TEXT_STRUCTURE_RA_TSLA", 0, 150, 1, 2, "0 1", "USSR");
public static readonly BuildingType FakeWeapon = new BuildingType(32, "weaf", "TEXT_STRUCTURE_RA_WEAF", 0, 2, 3, 2, null, "Greece", "weap2", "weap", BuildingTypeFlag.Bib | BuildingTypeFlag.Fake);
@ -103,9 +103,9 @@ namespace MobiusEditor.RedAlert
public static readonly BuildingType Barrel = new BuildingType(82, "barl", "TEXT_STRUCTURE_RA_BARL", 0, 0, 1, 1, null, "Neutral", BuildingTypeFlag.NoRemap);
public static readonly BuildingType Barrel3 = new BuildingType(83, "brl3", "TEXT_STRUCTURE_RA_BRL3", 0, 0, 1, 1, null, "Neutral", BuildingTypeFlag.NoRemap);
public static readonly BuildingType Queen = new BuildingType(84, "quee", "TEXT_STRUCTURE_RA_QUEE", 0, 0, 2, 1, null, "Special");
public static readonly BuildingType Larva1 = new BuildingType(85, "lar1", "TEXT_STRUCTURE_RA_LAR1", 0, 0, 1, 1, null, "Special", BuildingTypeFlag.NoRemap);
public static readonly BuildingType Larva2 = new BuildingType(86, "lar2", "TEXT_STRUCTURE_RA_LAR2", 0, 0, 1, 1, null, "Special", BuildingTypeFlag.NoRemap);
public static readonly BuildingType Queen = new BuildingType(84, "quee", "TEXT_STRUCTURE_RA_QUEE", 0, 0, 2, 1, null, "BadGuy");
public static readonly BuildingType Larva1 = new BuildingType(85, "lar1", "TEXT_STRUCTURE_RA_LAR1", 0, 0, 1, 1, null, "BadGuy", BuildingTypeFlag.NoRemap);
public static readonly BuildingType Larva2 = new BuildingType(86, "lar2", "TEXT_STRUCTURE_RA_LAR2", 0, 0, 1, 1, null, "BadGuy", BuildingTypeFlag.NoRemap);
private static readonly BuildingType[] Types;

View File

@ -442,6 +442,7 @@ namespace MobiusEditor.RedAlert
{
Bitmap mapImg = new Bitmap(Map.Metrics.Width * Globals.MapTileWidth, Map.Metrics.Height * Globals.MapTileHeight);
mapImg.SetResolution(96, 96);
mapImg.RemoveAlphaOnCurrent();
MapImage = mapImg;
}
}

View File

@ -0,0 +1,154 @@
namespace MobiusEditor.RedAlert
{
/// <summary>
/// Split off into its own thing to avoid creating clutter in the InfantryTypes class file.
/// </summary>
public static class InfantryClassicRemap
{
public static byte[] RemapCiv2 =
{
0,1,2,3,4,5,6,209,8,9,10,11,12,13,12,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
112,113,114,115,116,117,187,188,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,209, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,167, 13,189,190,191, // 176..191
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
public static byte[] RemapCiv4 = {
0,1,2,3,4,5,6,187,8,9,10,11,12,13,14,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,118,110,119, // 96..111
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191
192,193,194,195,196,197,198,199,200,201,202,203,204,205,188,207, // 192..207
208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
public static byte[] RemapCiv5 = {
0,1,2,3,4,5,6,109,8,9,10,11,131,13,14,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,177,110,178, // 96..111
112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191
192,193,194,195,196,197,198,199,111,201,202,203,204,205,111,207, // 192..207
208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
public static byte[] RemapCiv6 = {
0,1,2,3,4,5,6,120,8,9,10,11,12,13,238,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
112,113,114,115,116,117,236,206,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,111, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
public static byte[] RemapCiv7 = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,131,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
112,113,114,115,116,117,157,212,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,7, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,118,119,189,190,191, // 176..191
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
public static byte[] RemapCiv8 = {
0,1,2,3,4,5,6,182,8,9,10,11,12,13,131,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
112,113,114,115,116,117,215,7,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,182, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,198,199,189,190,191, // 176..191
192,193,194,195,196,197,198,199,111,201,202,203,204,205,206,207, // 192..207
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
public static byte[] RemapCiv9 = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,7,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
112,113,114,115,116,117,163,165,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,200, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,111,13,189,190,191, // 176..191
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
public static byte[] RemapCiv10 = {
0,1,2,3,4,5,6,137,8,9,10,11,12,13,15,15, // 0..15
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63
64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79
80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95
96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111
112,113,114,115,116,117,129,131,120,121,122,123,124,125,126,127, // 112..127
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143
144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,137, // 144..159
160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175
176,177,178,179,180,181,182,183,184,185,186,163,165,189,190,191, // 176..191
192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207
208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255
};
}
}

View File

@ -32,19 +32,19 @@ namespace MobiusEditor.RedAlert
public static readonly InfantryType Medic = new InfantryType(8, "medi", "TEXT_UNIT_RA_MEDI", "Greece", UnitTypeFlag.IsArmed);
public static readonly InfantryType General = new InfantryType(9, "gnrl", "TEXT_UNIT_RA_GNRL", "Greece", UnitTypeFlag.IsArmed);
public static readonly InfantryType Dog = new InfantryType(10, "dog", "TEXT_UNIT_RA_DOG", "USSR", UnitTypeFlag.IsArmed);
public static readonly InfantryType C1 = new InfantryType(11, "c1", "TEXT_UNIT_TITLE_CIV1", "Neutral", UnitTypeFlag.IsArmed);
public static readonly InfantryType C2 = new InfantryType(12, "c2", "TEXT_UNIT_TITLE_CIV10", "Neutral");
public static readonly InfantryType C3 = new InfantryType(13, "c3", "TEXT_UNIT_TITLE_CIV3", "Neutral");
public static readonly InfantryType C4 = new InfantryType(14, "c4", "TEXT_UNIT_TITLE_CIV4", "Neutral");
public static readonly InfantryType C5 = new InfantryType(15, "c5", "TEXT_UNIT_TITLE_CIV11", "Neutral");
public static readonly InfantryType C6 = new InfantryType(16, "c6", "TEXT_UNIT_TITLE_CIV12", "Neutral");
public static readonly InfantryType C7 = new InfantryType(17, "c7", "TEXT_UNIT_TITLE_CIV7", "Neutral", UnitTypeFlag.IsArmed);
public static readonly InfantryType C8 = new InfantryType(18, "c8", "TEXT_UNIT_TITLE_CIV8", "Neutral");
public static readonly InfantryType C9 = new InfantryType(19, "c9", "TEXT_UNIT_TITLE_CIV9", "Neutral");
public static readonly InfantryType C10 = new InfantryType(20, "c10", "TEXT_UNIT_RA_SCIENTIST", "Neutral");
public static readonly InfantryType Einstein = new InfantryType(21, "einstein", "TEXT_UNIT_RA_EINSTEIN", "Neutral");
public static readonly InfantryType Delphi = new InfantryType(22, "delphi", "TEXT_UNIT_RA_DELPHI", "Neutral", UnitTypeFlag.IsArmed);
public static readonly InfantryType DrChan = new InfantryType(23, "chan", "TEXT_UNIT_TITLE_CHAN", "Neutral");
public static readonly InfantryType C1 = new InfantryType(11, "c1", "TEXT_UNIT_TITLE_CIV1", "Neutral", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly InfantryType C2 = new InfantryType(12, "c2", "TEXT_UNIT_TITLE_CIV10", "Neutral", "c1", InfantryClassicRemap.RemapCiv2, UnitTypeFlag.NoRemap);
public static readonly InfantryType C3 = new InfantryType(13, "c3", "TEXT_UNIT_TITLE_CIV3", "Neutral", "c2", null, UnitTypeFlag.NoRemap);
public static readonly InfantryType C4 = new InfantryType(14, "c4", "TEXT_UNIT_TITLE_CIV4", "Neutral", "c2", InfantryClassicRemap.RemapCiv4, UnitTypeFlag.NoRemap);
public static readonly InfantryType C5 = new InfantryType(15, "c5", "TEXT_UNIT_TITLE_CIV11", "Neutral", "c2", InfantryClassicRemap.RemapCiv5, UnitTypeFlag.NoRemap);
public static readonly InfantryType C6 = new InfantryType(16, "c6", "TEXT_UNIT_TITLE_CIV12", "Neutral", "c1", InfantryClassicRemap.RemapCiv6, UnitTypeFlag.NoRemap);
public static readonly InfantryType C7 = new InfantryType(17, "c7", "TEXT_UNIT_TITLE_CIV7", "Neutral", "c1", InfantryClassicRemap.RemapCiv7, UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly InfantryType C8 = new InfantryType(18, "c8", "TEXT_UNIT_TITLE_CIV8", "Neutral", "c1", InfantryClassicRemap.RemapCiv8, UnitTypeFlag.NoRemap);
public static readonly InfantryType C9 = new InfantryType(19, "c9", "TEXT_UNIT_TITLE_CIV9", "Neutral", "c1", InfantryClassicRemap.RemapCiv10, UnitTypeFlag.NoRemap);
public static readonly InfantryType C10 = new InfantryType(20, "c10", "TEXT_UNIT_RA_SCIENTIST", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType Einstein = new InfantryType(21, "einstein", "TEXT_UNIT_RA_EINSTEIN", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType Delphi = new InfantryType(22, "delphi", "TEXT_UNIT_RA_DELPHI", "Neutral", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly InfantryType DrChan = new InfantryType(23, "chan", "TEXT_UNIT_TITLE_CHAN", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType ShockTrooper = new InfantryType(24, "shok", "TEXT_UNIT_RA_SHOK", "USSR", UnitTypeFlag.IsArmed | UnitTypeFlag.IsExpansionUnit);
public static readonly InfantryType Mechanic = new InfantryType(25, "mech", "TEXT_UNIT_RA_MECH", "Greece", UnitTypeFlag.IsArmed | UnitTypeFlag.IsExpansionUnit);

View File

@ -36,9 +36,9 @@ namespace MobiusEditor.RedAlert
public static readonly UnitType MCV = new UnitType(11, "mcv", "TEXT_UNIT_RA_MCV", "Greece");
public static readonly UnitType V2Launcher = new UnitType(12, "v2rl", "TEXT_UNIT_RA_V2RL", "USSR", UnitTypeFlag.IsArmed);
public static readonly UnitType ConvoyTruck = new UnitType(13, "truk", "TEXT_UNIT_RA_TRUK", "Greece");
public static readonly UnitType Ant1 = new UnitType(14, "ant1", "TEXT_UNIT_RA_ANT1", "Special", UnitTypeFlag.IsArmed);
public static readonly UnitType Ant2 = new UnitType(15, "ant2", "TEXT_UNIT_RA_ANT2", "Special", UnitTypeFlag.IsArmed);
public static readonly UnitType Ant3 = new UnitType(16, "ant3", "TEXT_UNIT_RA_ANT3", "Special", UnitTypeFlag.IsArmed);
public static readonly UnitType Ant1 = new UnitType(14, "ant1", "TEXT_UNIT_RA_ANT1", "BadGuy", UnitTypeFlag.IsArmed);
public static readonly UnitType Ant2 = new UnitType(15, "ant2", "TEXT_UNIT_RA_ANT2", "Ukraine", UnitTypeFlag.IsArmed);
public static readonly UnitType Ant3 = new UnitType(16, "ant3", "TEXT_UNIT_RA_ANT3", "Germany", UnitTypeFlag.IsArmed);
public static readonly UnitType Chrono = new UnitType(17, "ctnk", "TEXT_UNIT_RA_CTNK", "Greece", UnitTypeFlag.IsArmed | UnitTypeFlag.IsExpansionUnit);
public static readonly UnitType Tesla = new UnitType(18, "ttnk", "TEXT_UNIT_RA_TTNK", "USSR", UnitTypeFlag.IsArmed | UnitTypeFlag.HasTurret | UnitTypeFlag.IsExpansionUnit | UnitTypeFlag.IsJammer);
public static readonly UnitType MAD = new UnitType(19, "qtnk", "TEXT_UNIT_RA_QTNK", "USSR", UnitTypeFlag.IsArmed | UnitTypeFlag.IsExpansionUnit);

View File

@ -200,7 +200,6 @@ namespace MobiusEditor.Render
var renderBounds = new Rectangle(topLeft.X * tileSize.Width, topLeft.Y * tileSize.Height, tileSize.Width, tileSize.Height);
if(tile.Image != null)
{
//graphics.DrawImage(tile.Image, renderBounds);
using (Bitmap tileImg = tile.Image.RemoveAlpha())
{
graphics.DrawImage(tileImg, renderBounds);
@ -458,8 +457,15 @@ namespace MobiusEditor.Render
string tileName = terrain.Type.GraphicsSource;
if (!Globals.TheTilesetManager.GetTileData(tileName, terrain.Type.DisplayIcon, out Tile tile))
{
Debug.Print(string.Format("Terrain {0} ({1}) not found", tileName, terrain.Type.DisplayIcon));
return (Rectangle.Empty, (g) => { }, false);
// Fox for classic ore mine not using GraphicsSource override.
if (!String.Equals(terrain.Type.GraphicsSource, terrain.Type.Name, StringComparison.InvariantCultureIgnoreCase))
{
if (!Globals.TheTilesetManager.GetTileData(terrain.Type.Name, terrain.Type.DisplayIcon, out tile))
{
Debug.Print(string.Format("Terrain {0} ({1}) not found", tileName, terrain.Type.DisplayIcon));
return (Rectangle.Empty, (g) => { }, false);
}
}
}
var tint = terrain.Tint;
var imageAttributes = new ImageAttributes();
@ -504,7 +510,7 @@ namespace MobiusEditor.Render
{
maxIcon = Globals.TheTilesetManager.GetTileDataLength(building.Type.GraphicsSource);
hasCollapseFrame = (gameType == GameType.TiberianDawn || gameType == GameType.SoleSurvivor) && maxIcon > 1 && maxIcon % 2 == 1;
damageIconOffs = maxIcon / 2;
damageIconOffs = (maxIcon + (hasCollapseFrame ? 0 : 1)) / 2;
collapseIcon = maxIcon - 1;
}
if (building.Type.HasTurret)
@ -526,8 +532,9 @@ namespace MobiusEditor.Render
icon += damageIconOffs;
}
}
ITeamColor tc = building.Type.CanRemap ? Globals.TheTeamColorManager[building.House.BuildingTeamColor] : null;
if (Globals.TheTilesetManager.GetTeamColorTileData(building.Type.GraphicsSource, icon, tc, out Tile tile))
ITeamColor teamColor = building.Type.CanRemap ? Globals.TheTeamColorManager[building.House.BuildingTeamColor] : null;
Globals.TheTilesetManager.GetTeamColorTileData(building.Type.GraphicsSource, icon, teamColor, out Tile tile, true, false);
if (tile != null && tile.Image != null)
{
var location = new Point(topLeft.X * tileSize.Width, topLeft.Y * tileSize.Height);
var maxSize = new Size(building.Type.Size.Width * tileSize.Width, building.Type.Size.Height * tileSize.Height);
@ -543,7 +550,7 @@ namespace MobiusEditor.Render
int maxOverlayIcon = Globals.TheTilesetManager.GetTileDataLength(building.Type.FactoryOverlay);
overlayIcon = maxOverlayIcon / 2;
}
Globals.TheTilesetManager.GetTeamColorTileData(building.Type.FactoryOverlay, overlayIcon, Globals.TheTeamColorManager[building.House.BuildingTeamColor], out factoryOverlayTile);
Globals.TheTilesetManager.GetTeamColorTileData(building.Type.FactoryOverlay, overlayIcon, Globals.TheTeamColorManager[building.House.BuildingTeamColor], out factoryOverlayTile, true, false);
}
void render(Graphics g)
{
@ -569,6 +576,7 @@ namespace MobiusEditor.Render
factory.SetResolution(96, 96);
using (Graphics factoryG = Graphics.FromImage(factory))
{
factoryG.CopyRenderSettingsFrom(g);
var factBounds = RenderBounds(tile.Image.Size, building.Type.Size, tileScale);
var ovrlBounds = RenderBounds(factoryOverlayTile.Image.Size, building.Type.Size, tileScale);
factoryG.DrawImage(tile.Image, factBounds, 0, 0, tile.Image.Width, tile.Image.Height, GraphicsUnit.Pixel);
@ -597,8 +605,18 @@ namespace MobiusEditor.Render
public static (Rectangle, Action<Graphics>, bool) RenderInfantry(Point topLeft, Size tileSize, Infantry infantry, InfantryStoppingType infantryStoppingType)
{
var icon = HumanShape[Facing32[infantry.Direction.ID]];
string teamColor = infantry.House?.UnitTeamColor;
if (Globals.TheTilesetManager.GetTeamColorTileData(infantry.Type.Name, icon, Globals.TheTeamColorManager[teamColor], out Tile tile))
ITeamColor teamColor = infantry.Type.CanRemap ? Globals.TheTeamColorManager[infantry.House?.UnitTeamColor] : null;
Tile tile = null;
// RA classic infantry remap support.
if (infantry.Type.ClassicGraphicsSource != null && Globals.TheTilesetManager is TilesetManagerClassic tsmc)
{
tsmc.GetTeamColorTileData(infantry.Type.Name, icon, teamColor, out tile, true, false, infantry.Type.ClassicGraphicsSource, infantry.Type.ClassicGraphicsRemap);
}
else
{
Globals.TheTilesetManager.GetTeamColorTileData(infantry.Type.Name, icon, teamColor, out tile, true, false);
}
if (tile != null && tile.Image != null)
{
// These values are experimental, from comparing map editor screenshots to game screenshots. -Nyer
int infantryCorrectX = tileSize.Width / -12;
@ -724,15 +742,18 @@ namespace MobiusEditor.Render
{
icon = BodyShape[Facing32[unit.Direction.ID]];
}
string teamColor = null;
if (unit.House != null)
ITeamColor teamColor = null;
if (unit.House != null && unit.Type.CanRemap)
{
if (!unit.House.OverrideTeamColors.TryGetValue(unit.Type.Name, out teamColor))
String teamColorName;
if (!unit.House.OverrideTeamColors.TryGetValue(unit.Type.Name, out teamColorName))
{
teamColor = unit.House.UnitTeamColor;
teamColorName = unit.House.UnitTeamColor;
}
teamColor = Globals.TheTeamColorManager[teamColorName];
}
if (!Globals.TheTilesetManager.GetTeamColorTileData(unit.Type.Name, icon, Globals.TheTeamColorManager[teamColor], out Tile tile))
Globals.TheTilesetManager.GetTeamColorTileData(unit.Type.Name, icon, teamColor, out Tile tile, true, false);
if (tile == null || tile.Image == null)
{
Debug.Print(string.Format("Unit {0} ({1}) not found", unit.Type.Name, icon));
return (Rectangle.Empty, (g) => { }, false);
@ -795,9 +816,9 @@ namespace MobiusEditor.Render
turret2Icon = getRotorIcon(turret2Name, unit.Direction.ID, turret2Icon);
}
if (turretName != null)
Globals.TheTilesetManager.GetTeamColorTileData(turretName, turretIcon, Globals.TheTeamColorManager[teamColor], out turretTile);
Globals.TheTilesetManager.GetTeamColorTileData(turretName, turretIcon, teamColor, out turretTile, true, false);
if (turret2Name != null)
Globals.TheTilesetManager.GetTeamColorTileData(turret2Name, turret2Icon, Globals.TheTeamColorManager[teamColor], out turret2Tile);
Globals.TheTilesetManager.GetTeamColorTileData(turret2Name, turret2Icon, teamColor, out turret2Tile, true, false);
}
var tint = unit.Tint;
void render(Graphics g)
@ -822,6 +843,7 @@ namespace MobiusEditor.Render
unitBm.SetResolution(96, 96);
using (Graphics unitG = Graphics.FromImage(unitBm))
{
unitG.CopyRenderSettingsFrom(g);
if (tile != null) {
unitG.DrawImage(tile.Image, renderRect, 0, 0, tile.Image.Width, tile.Image.Height, GraphicsUnit.Pixel);
}
@ -887,11 +909,11 @@ namespace MobiusEditor.Render
ug.DrawImage(turrTile.Image, turrBounds, 0, 0, turrTile.Image.Width, turrTile.Image.Height, GraphicsUnit.Pixel);
}
if (turretTile != null)
if (turretTile != null && turretTile.Image != null)
{
RenderTurret(unitG, turretTile, turretAdjust, tileSize);
}
if (unit.Type.HasDoubleTurret && turret2Tile != null)
if (unit.Type.HasDoubleTurret && turret2Tile != null && turret2Tile.Image != null)
{
RenderTurret(unitG, turret2Tile, turret2Adjust, tileSize);
}
@ -926,7 +948,7 @@ namespace MobiusEditor.Render
int mpId = Waypoint.GetMpIdFromFlag(waypoint.Flag);
int icon = 0;
bool isDefaultIcon = true;
bool gotIcon = false;
bool gotTile = false;
Tile tile;
if (!soloMission && mpId >= 0 && mpId < flagColors.Length)
{
@ -935,7 +957,7 @@ namespace MobiusEditor.Render
// Always paint flags as opaque.
transparencyModifier = 1.0f;
teamColor = flagColors[mpId];
gotIcon = Globals.TheTilesetManager.GetTeamColorTileData(tileGraphics, icon, teamColor, out tile);
gotTile = Globals.TheTilesetManager.GetTeamColorTileData(tileGraphics, icon, teamColor, out tile);
}
else if (gameType == GameType.SoleSurvivor && (waypoint.Flag & WaypointFlag.CrateSpawn) == WaypointFlag.CrateSpawn)
{
@ -943,20 +965,20 @@ namespace MobiusEditor.Render
tileGraphics = "scrate";
//tint = Color.FromArgb(waypoint.Tint.A, Color.Green);
//brightness = 1.5f;
gotIcon = Globals.TheTilesetManager.GetTileData(tileGraphics, icon, out tile);
gotTile = Globals.TheTilesetManager.GetTileData(tileGraphics, icon, out tile);
}
else
{
gotIcon = Globals.TheTilesetManager.GetTileData(tileGraphics, icon, out tile);
gotTile = Globals.TheTilesetManager.GetTileData(tileGraphics, icon, out tile);
}
if (!gotIcon && isDefaultIcon)
if (!gotTile && isDefaultIcon)
{
// Beacon only exists in remastered graphics. Get fallback.
tileGraphics = "armor";
icon = 6;
gotIcon = Globals.TheTilesetManager.GetTeamColorTileData(tileGraphics, icon, teamColor, out tile);
tileGraphics = "trans.icn";
icon = 3;
gotTile = Globals.TheTilesetManager.GetTeamColorTileData(tileGraphics, icon, teamColor, out tile);
}
if (!gotIcon)
if (!gotTile)
{
Debug.Print(string.Format("Waypoint graphics {0} ({1}) not found", tileGraphics, icon));
return (Rectangle.Empty, (g) => { });
@ -1113,7 +1135,7 @@ namespace MobiusEditor.Render
RegionData paintAreaRel;
if (!paintAreas.TryGetValue(ovlt.Name, out paintAreaRel))
{
paintAreaRel = GetOverlayOutline(map.Theater, location, tileSize, tileScale, overlay, outlineThickness, alphaThreshold, true);
paintAreaRel = GetOverlayOutline(location, tileSize, tileScale, overlay, outlineThickness, alphaThreshold, true);
paintAreas[ovlt.Name] = paintAreaRel;
}
int actualTopLeftX = location.X * tileSize.Width;
@ -1183,7 +1205,7 @@ namespace MobiusEditor.Render
return false;
}
public static RegionData GetOverlayOutline(TheaterType theater, Point topLeft, Size tileSize, double tileScale, Overlay overlay, float outline, byte alphaThreshold, bool relative)
public static RegionData GetOverlayOutline(Point topLeft, Size tileSize, double tileScale, Overlay overlay, float outline, byte alphaThreshold, bool relative)
{
OverlayType ovtype = overlay.Type;
string name = ovtype.GraphicsSource;
@ -1200,84 +1222,87 @@ namespace MobiusEditor.Render
Size maxSize = new Size(Globals.OriginalTileWidth, Globals.OriginalTileHeight);
int actualOutlineX = (int)Math.Max(1, outline * tileSize.Width);
int actualOutlineY = (int)Math.Max(1, outline * tileSize.Height);
Byte[] imgData;
int stride;
// Make full-tile image out of the graphics.
using (Bitmap bm = new Bitmap(tileSize.Width, tileSize.Height, PixelFormat.Format32bppArgb))
{
bm.SetResolution(96, 96);
using (Graphics g2 = Graphics.FromImage(bm))
{
SetRenderSettings(g2, false);
g2.DrawImage(tile.Image, relOverlayBounds, 0, 0, tile.Image.Width, tile.Image.Height, GraphicsUnit.Pixel);
}
Byte[] imgData = ImageUtils.GetImageData(bm, out int stride, PixelFormat.Format32bppArgb, true);
bool isOpaqueAndNotBlack(byte[] mapdata, int yVal, int xVal)
{
int address = yVal * stride + xVal * 4;
// Check alpha
if (mapdata[address + 3] < alphaThreshold)
{
return false;
}
// Check brightness to exclude shadow
byte red = mapdata[address + 2];
byte grn = mapdata[address + 1];
byte blu = mapdata[address + 0];
// Integer method.
int redBalanced = red * red * 2126;
int grnBalanced = grn * grn * 7152;
int bluBalanced = blu * blu * 0722;
int lum = (redBalanced + grnBalanced + bluBalanced) / 255 / 255;
// The integer division will automatically reduce anything near-black
// to zero, so actually checking against a threshold is unnecessary.
return lum > 0; // lum > lumThresholdSq * 1000
// Floating point method
//double redF = red / 255.0;
//double grnF = grn / 255.0;
//double bluF = blu / 255.0;
//double lum = 0.2126d * redF * redF + 0.7152d * grnF * grnF + 0.0722d * bluF * bluF;
//return lum >= lumThresholdSq;
};
//Func<byte[], int, int, bool> isOpaque_ = (mapdata, yVal, xVal) => mapdata[yVal * stride + xVal * 4 + 3] >= alphaThreshold;
List<List<Point>> blobs = BlobDetection.FindBlobs(imgData, tileSize.Width, tileSize.Height, isOpaqueAndNotBlack, true, false);
List<Point> allblobs = new List<Point>();
foreach (List<Point> blob in blobs)
{
foreach (Point p in blob)
{
allblobs.Add(p);
}
}
HashSet<Point> drawPoints = new HashSet<Point>();
HashSet<Point> removePoints = new HashSet<Point>();
foreach (Point p in allblobs)
{
Rectangle rect = new Rectangle(p.X + actualTopLeftX, p.Y + actualTopLeftY, 1, 1);
removePoints.UnionWith(rect.Points());
rect.Inflate(actualOutlineX, actualOutlineY);
rect.Intersect(overlayBounds);
if (!rect.IsEmpty)
{
drawPoints.UnionWith(rect.Points());
}
}
foreach (Point p in removePoints)
{
drawPoints.Remove(p);
}
RegionData rData;
using (Region r = new Region())
{
r.MakeEmpty();
Size pixelSize = new Size(1, 1);
foreach (Point p in drawPoints)
{
r.Union(new Rectangle(p, pixelSize));
}
rData = r.GetRegionData();
}
return rData;
imgData = ImageUtils.GetImageData(bm, out stride, PixelFormat.Format32bppArgb, true);
}
bool isOpaqueAndNotBlack(byte[] mapdata, int yVal, int xVal)
{
int address = yVal * stride + xVal * 4;
// Check alpha
if (mapdata[address + 3] < alphaThreshold)
{
return false;
}
// Check brightness to exclude shadow
byte red = mapdata[address + 2];
byte grn = mapdata[address + 1];
byte blu = mapdata[address + 0];
// Integer method.
int redBalanced = red * red * 2126;
int grnBalanced = grn * grn * 7152;
int bluBalanced = blu * blu * 0722;
int lum = (redBalanced + grnBalanced + bluBalanced) / 255 / 255;
// The integer division will automatically reduce anything near-black
// to zero, so actually checking against a threshold is unnecessary.
return lum > 0; // lum > lumThresholdSq * 1000
// Floating point method
//double redF = red / 255.0;
//double grnF = grn / 255.0;
//double bluF = blu / 255.0;
//double lum = 0.2126d * redF * redF + 0.7152d * grnF * grnF + 0.0722d * bluF * bluF;
//return lum >= lumThresholdSq;
};
//Func<byte[], int, int, bool> isOpaque_ = (mapdata, yVal, xVal) => mapdata[yVal * stride + xVal * 4 + 3] >= alphaThreshold;
List<List<Point>> blobs = BlobDetection.FindBlobs(imgData, tileSize.Width, tileSize.Height, isOpaqueAndNotBlack, true, false);
List<Point> allblobs = new List<Point>();
foreach (List<Point> blob in blobs)
{
foreach (Point p in blob)
{
allblobs.Add(p);
}
}
HashSet<Point> drawPoints = new HashSet<Point>();
HashSet<Point> removePoints = new HashSet<Point>();
foreach (Point p in allblobs)
{
Rectangle rect = new Rectangle(p.X + actualTopLeftX, p.Y + actualTopLeftY, 1, 1);
removePoints.UnionWith(rect.Points());
rect.Inflate(actualOutlineX, actualOutlineY);
rect.Intersect(overlayBounds);
if (!rect.IsEmpty)
{
drawPoints.UnionWith(rect.Points());
}
}
foreach (Point p in removePoints)
{
drawPoints.Remove(p);
}
RegionData rData;
using (Region r = new Region())
{
r.MakeEmpty();
Size pixelSize = new Size(1, 1);
foreach (Point p in drawPoints)
{
r.Union(new Rectangle(p, pixelSize));
}
rData = r.GetRegionData();
}
return rData;
}
public static void RenderAllFootballAreas(Graphics graphics, Map map, Size tileSize, double tileScale, GameType gameType)
@ -1337,15 +1362,15 @@ namespace MobiusEditor.Render
}
}
public static void RenderAllFakeBuildingLabels(Graphics graphics, Map map, Size tileSize, double tileScale)
public static void RenderAllFakeBuildingLabels(Graphics graphics, Map map, Size tileSize)
{
foreach (var (topLeft, building) in map.Buildings.OfType<Building>())
{
RenderFakeBuildingLabel(graphics, building, topLeft, tileSize, tileScale, false);
RenderFakeBuildingLabel(graphics, building, topLeft, tileSize, false);
}
}
public static void RenderFakeBuildingLabel(Graphics graphics, Building building, Point topLeft, Size tileSize, double tileScale, Boolean forPreview)
public static void RenderFakeBuildingLabel(Graphics graphics, Building building, Point topLeft, Size tileSize, Boolean forPreview)
{
if (!building.Type.IsFake)
{
@ -1362,11 +1387,12 @@ namespace MobiusEditor.Render
new Size(maxSize.Width * tileSize.Width, maxSize.Height * tileSize.Height)
);
string fakeText = Globals.TheGameTextManager["TEXT_UI_FAKE"];
double tileScaleHor = tileSize.Width / 128.0;
using (var fakeBackgroundBrush = new SolidBrush(Color.FromArgb((forPreview ? 128 : 256) * 2 / 3, Color.Black)))
using (var fakeTextBrush = new SolidBrush(Color.FromArgb(forPreview ? building.Tint.A : 255, Color.White)))
{
using (var font = graphics.GetAdjustedFont(fakeText, SystemFonts.DefaultFont, buildingBounds.Width, buildingBounds.Height,
Math.Max(1, (int)(12 * tileScale)), Math.Max(1, (int)(24 * tileScale)), stringFormat, true))
Math.Max(1, (int)(12 * tileScaleHor)), Math.Max(1, (int)(24 * tileScaleHor)), stringFormat, true))
{
var textBounds = graphics.MeasureString(fakeText, font, buildingBounds.Width, stringFormat);
var backgroundBounds = new RectangleF(buildingBounds.Location, textBounds);
@ -1376,15 +1402,15 @@ namespace MobiusEditor.Render
}
}
public static void RenderAllRebuildPriorityLabels(Graphics graphics, Map map, Size tileSize, double tileScale)
public static void RenderAllRebuildPriorityLabels(Graphics graphics, Map map, Size tileSize)
{
foreach (var (topLeft, building) in map.Buildings.OfType<Building>())
{
RenderRebuildPriorityLabel(graphics, building, topLeft, tileSize, tileScale, false);
RenderRebuildPriorityLabel(graphics, building, topLeft, tileSize, false);
}
}
public static void RenderRebuildPriorityLabel(Graphics graphics, Building building, Point topLeft, Size tileSize, double tileScale, Boolean forPreview)
public static void RenderRebuildPriorityLabel(Graphics graphics, Building building, Point topLeft, Size tileSize, Boolean forPreview)
{
var stringFormat = new StringFormat
{
@ -1398,12 +1424,13 @@ namespace MobiusEditor.Render
);
if (building.BasePriority >= 0)
{
double tileScaleHor = tileSize.Width / 128.0;
string priText = building.BasePriority.ToString();
using (var baseBackgroundBrush = new SolidBrush(Color.FromArgb((forPreview ? 128 : 256) * 2 / 3, Color.Black)))
using (var baseTextBrush = new SolidBrush(Color.FromArgb(forPreview ? 128 : 255, Color.Red)))
{
using (var font = graphics.GetAdjustedFont(priText, SystemFonts.DefaultFont, buildingBounds.Width, buildingBounds.Height,
Math.Max(1, (int)(12 * tileScale)), Math.Max(1, (int)(24 * tileScale)), stringFormat, true))
Math.Max(1, (int)(12 * tileScaleHor)), Math.Max(1, (int)(24 * tileScaleHor)), stringFormat, true))
{
var textBounds = graphics.MeasureString(priText, font, buildingBounds.Width, stringFormat);
var backgroundBounds = new RectangleF(buildingBounds.Location, textBounds);
@ -1415,13 +1442,14 @@ namespace MobiusEditor.Render
}
}
public static void RenderAllTechnoTriggers(Graphics graphics, Map map, Size tileSize, double tileScale, MapLayerFlag layersToRender)
public static void RenderAllTechnoTriggers(Graphics graphics, Map map, Size tileSize, MapLayerFlag layersToRender)
{
RenderAllTechnoTriggers(graphics, map, tileSize, tileScale, layersToRender, Color.LimeGreen, null, false);
RenderAllTechnoTriggers(graphics, map, tileSize, layersToRender, Color.LimeGreen, null, false);
}
public static void RenderAllTechnoTriggers(Graphics graphics, Map map, Size tileSize, double tileScale, MapLayerFlag layersToRender, Color color, string toPick, bool excludePick)
public static void RenderAllTechnoTriggers(Graphics graphics, Map map, Size tileSize, MapLayerFlag layersToRender, Color color, string toPick, bool excludePick)
{
double tileScaleHor = tileSize.Width / 128.0;
float borderSize = Math.Max(0.5f, tileSize.Width / 60.0f);
foreach (var (cell, techno) in map.Technos)
{
@ -1504,7 +1532,7 @@ namespace MobiusEditor.Render
using (var technoTriggerBrush = new SolidBrush(alphaColor))
using (var technoTriggerPen = new Pen(alphaColor, borderSize))
using (var font = graphics.GetAdjustedFont(trigger, SystemFonts.DefaultFont, bounds.Width, bounds.Height,
Math.Max(1, (int)(12 * tileScale)), Math.Max(1, (int)(24 * tileScale)), stringFormat, true))
Math.Max(1, (int)(12 * tileScaleHor)), Math.Max(1, (int)(24 * tileScaleHor)), stringFormat, true))
{
var textBounds = graphics.MeasureString(trigger, font, bounds.Width, stringFormat);
var backgroundBounds = new RectangleF(bounds.Location, textBounds);
@ -1518,7 +1546,7 @@ namespace MobiusEditor.Render
}
}
public static void RenderWayPointIndicators(Graphics graphics, Map map, Size tileSize, double tileScale, Color textColor, bool forPreview, bool excludeSpecified, params Waypoint[] specified)
public static void RenderWayPointIndicators(Graphics graphics, Map map, Size tileSize, Color textColor, bool forPreview, bool excludeSpecified, params Waypoint[] specified)
{
HashSet<Waypoint> specifiedWaypoints = specified.ToHashSet();
@ -1531,12 +1559,12 @@ namespace MobiusEditor.Render
{
continue;
}
RenderWayPointIndicator(graphics, waypoint, point, tileSize, tileScale, textColor, forPreview);
RenderWayPointIndicator(graphics, waypoint, point, tileSize, textColor, forPreview);
}
}
}
public static void RenderWayPointIndicator(Graphics graphics, Waypoint waypoint, Point topLeft, Size tileSize, double tileScale, Color textColor, bool forPreview)
public static void RenderWayPointIndicator(Graphics graphics, Waypoint waypoint, Point topLeft, Size tileSize, Color textColor, bool forPreview)
{
var stringFormat = new StringFormat
{
@ -1544,12 +1572,14 @@ namespace MobiusEditor.Render
LineAlignment = StringAlignment.Center
};
var paintBounds = new Rectangle(new Point(topLeft.X * tileSize.Width, topLeft.Y * tileSize.Height), tileSize);
// Adjust calcuations to tile size. The below adjustments are done assuming the tile is 128 wide.
double tileScaleHor = tileSize.Width / 128.0;
string wpText = waypoint.Name;
using (var baseBackgroundBrush = new SolidBrush(Color.FromArgb(forPreview ? 64 : 128, Color.Black)))
using (var baseTextBrush = new SolidBrush(Color.FromArgb(forPreview ? 128 : 255, textColor)))
{
using (var font = graphics.GetAdjustedFont(wpText, SystemFonts.DefaultFont, paintBounds.Width, paintBounds.Height,
Math.Max(1, (int)(12 * tileScale)), Math.Max(1, (int)(55 * tileScale)), stringFormat, true))
Math.Max(1, (int)(12 * tileScaleHor)), Math.Max(1, (int)(55 * tileScaleHor)), stringFormat, true))
{
var textBounds = graphics.MeasureString(wpText, font, paintBounds.Width, stringFormat);
var backgroundBounds = new RectangleF(paintBounds.Location, textBounds);
@ -1721,23 +1751,24 @@ namespace MobiusEditor.Render
}
}
public static void RenderCellTriggersSoft(Graphics graphics, Map map, Size tileSize, double tileScale, params String[] specifiedToExclude)
public static void RenderCellTriggersSoft(Graphics graphics, Map map, Size tileSize, params String[] specifiedToExclude)
{
RenderCellTriggers(graphics, map, tileSize, tileScale, Color.Black, Color.White, Color.White, 0.75f, false, true, specifiedToExclude);
RenderCellTriggers(graphics, map, tileSize, Color.Black, Color.White, Color.White, 0.75f, false, true, specifiedToExclude);
}
public static void RenderCellTriggersHard(Graphics graphics, Map map, Size tileSize, double tileScale, params String[] specifiedToExclude)
public static void RenderCellTriggersHard(Graphics graphics, Map map, Size tileSize, params String[] specifiedToExclude)
{
RenderCellTriggers(graphics, map, tileSize, tileScale, Color.Black, Color.White, Color.White, 1, false, true, specifiedToExclude);
RenderCellTriggers(graphics, map, tileSize, Color.Black, Color.White, Color.White, 1, false, true, specifiedToExclude);
}
public static void RenderCellTriggersSelected(Graphics graphics, Map map, Size tileSize, double tileScale, params String[] specifiedToDraw)
public static void RenderCellTriggersSelected(Graphics graphics, Map map, Size tileSize, params String[] specifiedToDraw)
{
RenderCellTriggers(graphics, map, tileSize, tileScale, Color.Black, Color.Yellow, Color.Yellow, 1, true, false, specifiedToDraw);
RenderCellTriggers(graphics, map, tileSize, Color.Black, Color.Yellow, Color.Yellow, 1, true, false, specifiedToDraw);
}
public static void RenderCellTriggers(Graphics graphics, Map map, Size tileSize, double tileScale, Color fillColor, Color borderColor, Color textColor, double alphaAdjust, bool thickborder, bool excludeSpecified, params String[] specified)
public static void RenderCellTriggers(Graphics graphics, Map map, Size tileSize, Color fillColor, Color borderColor, Color textColor, double alphaAdjust, bool thickborder, bool excludeSpecified, params String[] specified)
{
double tileScaleHor = tileSize.Width / 128.0;
Color ApplyAlpha(Color col, int baseAlpha, double alphaMul)
{
return Color.FromArgb(Math.Max(0, Math.Min(0xFF, (int)Math.Round(baseAlpha * alphaMul, MidpointRounding.AwayFromZero))), col);
@ -1782,7 +1813,7 @@ namespace MobiusEditor.Render
};
var text = cellTrigger.Trigger;
using (var font = graphics.GetAdjustedFont(text, SystemFonts.DefaultFont, textBounds.Width, textBounds.Height,
Math.Max(1, (int)(24 * tileScale)), Math.Max(1, (int)(48 * tileScale)), stringFormat, true))
Math.Max(1, (int)(24 * tileScaleHor)), Math.Max(1, (int)(48 * tileScaleHor)), stringFormat, true))
{
graphics.DrawString(text.ToString(), font, isPreview ? prevCellTriggersBrush : cellTriggersBrush, textBounds, stringFormat);
}
@ -1861,6 +1892,14 @@ namespace MobiusEditor.Render
}
}
public static void CopyRenderSettingsFrom(this Graphics target, Graphics source)
{
target.CompositingQuality = source.CompositingQuality;
target.InterpolationMode = source.InterpolationMode;
target.SmoothingMode = source.SmoothingMode;
target.PixelOffsetMode = source.PixelOffsetMode;
}
public static Rectangle RenderBounds(Size imageSize, Size cellDimensions, Size cellSize)
{
double scaleFactorX = cellSize.Width / (double)Globals.OriginalTileWidth;

View File

@ -413,6 +413,7 @@ namespace MobiusEditor.TiberianDawn
{
Bitmap mapImg = new Bitmap(Map.Metrics.Width * Globals.MapTileWidth, Map.Metrics.Height * Globals.MapTileHeight);
mapImg.SetResolution(96, 96);
mapImg.RemoveAlphaOnCurrent();
MapImage = mapImg;
}
}

View File

@ -28,19 +28,19 @@ namespace MobiusEditor.TiberianDawn
public static readonly InfantryType E5 = new InfantryType(4, "e5", "TEXT_UNIT_TITLE_NOD_CHEM_WARRIOR", "Badguy", UnitTypeFlag.IsArmed);
public static readonly InfantryType E7 = new InfantryType(5, "e6", "TEXT_UNIT_TITLE_GDI_ENGINEER", "Goodguy");
public static readonly InfantryType Commando = new InfantryType(6, "rmbo", "TEXT_UNIT_TITLE_GDI_COMMANDO", "Goodguy", UnitTypeFlag.IsArmed);
public static readonly InfantryType C1 = new InfantryType(7, "c1", "TEXT_UNIT_TITLE_CIV1", "Neutral", UnitTypeFlag.IsArmed);
public static readonly InfantryType C2 = new InfantryType(8, "c2", "TEXT_UNIT_TITLE_CIV2", "Neutral");
public static readonly InfantryType C3 = new InfantryType(9, "c3", "TEXT_UNIT_TITLE_CIV3", "Neutral");
public static readonly InfantryType C4 = new InfantryType(10, "c4", "TEXT_UNIT_TITLE_CIV4", "Neutral");
public static readonly InfantryType C5 = new InfantryType(11, "c5", "TEXT_UNIT_TITLE_CIV5", "Neutral");
public static readonly InfantryType C6 = new InfantryType(12, "c6", "TEXT_UNIT_TITLE_CIV6", "Neutral");
public static readonly InfantryType C7 = new InfantryType(13, "c7", "TEXT_UNIT_TITLE_CIV7", "Neutral", UnitTypeFlag.IsArmed);
public static readonly InfantryType C8 = new InfantryType(14, "c8", "TEXT_UNIT_TITLE_CIV8", "Neutral");
public static readonly InfantryType C9 = new InfantryType(15, "c9", "TEXT_UNIT_TITLE_CIV9", "Neutral");
public static readonly InfantryType C10 = new InfantryType(16, "c10", "TEXT_UNIT_TITLE_C10", "Neutral");
public static readonly InfantryType Moebius = new InfantryType(17, "moebius", "TEXT_UNIT_TITLE_MOEBIUS", "Neutral");
public static readonly InfantryType Delphi = new InfantryType(18, "delphi", "TEXT_UNIT_TITLE_DELPHI", "Neutral", UnitTypeFlag.IsArmed);
public static readonly InfantryType DrChan = new InfantryType(19, "chan", "TEXT_UNIT_TITLE_CHAN", "Neutral");
public static readonly InfantryType C1 = new InfantryType(7, "c1", "TEXT_UNIT_TITLE_CIV1", "Neutral", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly InfantryType C2 = new InfantryType(8, "c2", "TEXT_UNIT_TITLE_CIV2", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType C3 = new InfantryType(9, "c3", "TEXT_UNIT_TITLE_CIV3", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType C4 = new InfantryType(10, "c4", "TEXT_UNIT_TITLE_CIV4", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType C5 = new InfantryType(11, "c5", "TEXT_UNIT_TITLE_CIV5", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType C6 = new InfantryType(12, "c6", "TEXT_UNIT_TITLE_CIV6", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType C7 = new InfantryType(13, "c7", "TEXT_UNIT_TITLE_CIV7", "Neutral", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly InfantryType C8 = new InfantryType(14, "c8", "TEXT_UNIT_TITLE_CIV8", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType C9 = new InfantryType(15, "c9", "TEXT_UNIT_TITLE_CIV9", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType C10 = new InfantryType(16, "c10", "TEXT_UNIT_TITLE_C10", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType Moebius = new InfantryType(17, "moebius", "TEXT_UNIT_TITLE_MOEBIUS", "Neutral", UnitTypeFlag.NoRemap);
public static readonly InfantryType Delphi = new InfantryType(18, "delphi", "TEXT_UNIT_TITLE_DELPHI", "Neutral", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly InfantryType DrChan = new InfantryType(19, "chan", "TEXT_UNIT_TITLE_CHAN", "Neutral", UnitTypeFlag.NoRemap);
private static readonly InfantryType[] Types;

View File

@ -40,10 +40,10 @@ namespace MobiusEditor.TiberianDawn
public static readonly UnitType GunBoat = new UnitType(15, "boat", "TEXT_UNIT_TITLE_WAKE", "Goodguy", UnitTypeFlag.IsArmed);
public static readonly UnitType MCV = new UnitType(16, "mcv", "TEXT_UNIT_TITLE_GDI_MCV", "Goodguy");
public static readonly UnitType Bike = new UnitType(17, "bike", "TEXT_UNIT_TITLE_NOD_RECON_BIKE", "Badguy", UnitTypeFlag.IsArmed);
public static readonly UnitType Tric = new UnitType(18, "tric", "TEXT_UNIT_TITLE_TRIC", "Special", UnitTypeFlag.IsArmed);
public static readonly UnitType Trex = new UnitType(19, "trex", "TEXT_UNIT_TITLE_TREX", "Special", UnitTypeFlag.IsArmed);
public static readonly UnitType Rapt = new UnitType(20, "rapt", "TEXT_UNIT_TITLE_RAPT", "Special", UnitTypeFlag.IsArmed);
public static readonly UnitType Steg = new UnitType(21, "steg", "TEXT_UNIT_TITLE_STEG", "Special", UnitTypeFlag.IsArmed);
public static readonly UnitType Tric = new UnitType(18, "tric", "TEXT_UNIT_TITLE_TRIC", "Special", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly UnitType Trex = new UnitType(19, "trex", "TEXT_UNIT_TITLE_TREX", "Special", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly UnitType Rapt = new UnitType(20, "rapt", "TEXT_UNIT_TITLE_RAPT", "Special", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly UnitType Steg = new UnitType(21, "steg", "TEXT_UNIT_TITLE_STEG", "Special", UnitTypeFlag.IsArmed | UnitTypeFlag.NoRemap);
public static readonly UnitType Tran = new UnitType(0 | UnitTypeIDMask.Aircraft, "tran", "TEXT_UNIT_TITLE_GDI_TRANSPORT", "Goodguy", "LROTOR", "RROTOR", 1, -2, UnitTypeFlag.HasTurret | UnitTypeFlag.HasDoubleTurret);
public static readonly UnitType A10 = new UnitType(1 | UnitTypeIDMask.Aircraft, "a10", "TEXT_UNIT_TITLE_A10", "Goodguy", UnitTypeFlag.IsArmed | UnitTypeFlag.IsFixedWing);

View File

@ -759,11 +759,11 @@ namespace MobiusEditor.Tools
MapRenderer.RenderAllOccupierBounds(g, Globals.PreviewTileSize, buildingList);
if ((Layers & MapLayerFlag.BuildingFakes) == MapLayerFlag.BuildingFakes)
{
MapRenderer.RenderFakeBuildingLabel(g, mockBuilding, new Point(0, 0), Globals.PreviewTileSize, Globals.PreviewTileScale, false);
MapRenderer.RenderFakeBuildingLabel(g, mockBuilding, new Point(0, 0), Globals.PreviewTileSize, false);
}
if ((Layers & MapLayerFlag.BuildingRebuild) == MapLayerFlag.BuildingRebuild)
{
MapRenderer.RenderRebuildPriorityLabel(g, mockBuilding, new Point(0, 0), Globals.PreviewTileSize, Globals.PreviewTileScale, false);
MapRenderer.RenderRebuildPriorityLabel(g, mockBuilding, new Point(0, 0), Globals.PreviewTileSize, false);
}
}
buildingTypeMapPanel.MapImage = buildingPreview;
@ -858,15 +858,15 @@ namespace MobiusEditor.Tools
}
if ((Layers & MapLayerFlag.BuildingFakes) == MapLayerFlag.BuildingFakes)
{
MapRenderer.RenderAllFakeBuildingLabels(graphics, previewMap, Globals.MapTileSize, Globals.MapTileScale);
MapRenderer.RenderAllFakeBuildingLabels(graphics, previewMap, Globals.MapTileSize);
}
if ((Layers & MapLayerFlag.BuildingRebuild) == MapLayerFlag.BuildingRebuild)
{
MapRenderer.RenderAllRebuildPriorityLabels(graphics, previewMap, Globals.MapTileSize, Globals.MapTileScale);
MapRenderer.RenderAllRebuildPriorityLabels(graphics, previewMap, Globals.MapTileSize);
}
if ((Layers & MapLayerFlag.TechnoTriggers) == MapLayerFlag.TechnoTriggers)
{
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Globals.MapTileScale, Layers);
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Layers);
}
}
}

View File

@ -486,14 +486,14 @@ namespace MobiusEditor.Tools
selected = null;
string[] selectedRange = selected != null ? new[] { selected } : new string[] { };
// Normal techno triggers: under cell
MapRenderer.RenderAllTechnoTriggers(graphics, map, Globals.MapTileSize, Globals.MapTileScale, Layers, Color.LimeGreen, selected, true);
MapRenderer.RenderCellTriggersHard(graphics, map, Globals.MapTileSize, Globals.MapTileScale, selectedRange);
MapRenderer.RenderAllTechnoTriggers(graphics, map, Globals.MapTileSize, Layers, Color.LimeGreen, selected, true);
MapRenderer.RenderCellTriggersHard(graphics, map, Globals.MapTileSize, selectedRange);
if (selected != null)
{
// Only use preview map if in placement mode.
MapRenderer.RenderCellTriggersSelected(graphics, placementMode ? previewMap : map, Globals.MapTileSize, Globals.MapTileScale, selectedRange);
MapRenderer.RenderCellTriggersSelected(graphics, placementMode ? previewMap : map, Globals.MapTileSize, selectedRange);
// Selected technos: on top of cell
MapRenderer.RenderAllTechnoTriggers(graphics, map, Globals.MapTileSize, Globals.MapTileScale, Layers, Color.Yellow, selected, false);
MapRenderer.RenderAllTechnoTriggers(graphics, map, Globals.MapTileSize, Layers, Color.Yellow, selected, false);
}
}

View File

@ -812,7 +812,7 @@ namespace MobiusEditor.Tools
MapRenderer.RenderAllBoundsFromPoint(graphics, Globals.MapTileSize, previewMap.Technos.OfType<InfantryGroup>());
if ((Layers & (MapLayerFlag.Infantry | MapLayerFlag.TechnoTriggers)) == (MapLayerFlag.Infantry | MapLayerFlag.TechnoTriggers))
{
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Globals.MapTileScale, Layers);
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Layers);
}
}

View File

@ -957,7 +957,7 @@ namespace MobiusEditor.Tools
}
templateTypeMapPanel.MapImage = selected.Thumbnail;
var templateTypeMetrics = new CellMetrics(selected.ThumbnailIconWidth, selected.ThumbnailIconHeight);
templateTypeNavigationWidget = new NavigationWidget(templateTypeMapPanel, templateTypeMetrics, Globals.OriginalTileSize, false);
templateTypeNavigationWidget = new NavigationWidget(templateTypeMapPanel, templateTypeMetrics, Globals.PreviewTileSize, false);
templateTypeNavigationWidget.MouseoverSize = Size.Empty;
templateTypeNavigationWidget.Activate();
}

View File

@ -580,7 +580,7 @@ namespace MobiusEditor.Tools
MapRenderer.RenderAllOccupierBounds(graphics, Globals.MapTileSize, previewMap.Technos.OfType<Terrain>());
if ((Layers & MapLayerFlag.TechnoTriggers) == MapLayerFlag.TechnoTriggers)
{
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Globals.MapTileScale, Layers);
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Layers);
}
}

View File

@ -613,7 +613,7 @@ namespace MobiusEditor.Tools
{
if ((Layers & MapLayerFlag.TechnoTriggers) == MapLayerFlag.TechnoTriggers)
{
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Globals.MapTileScale, Layers);
MapRenderer.RenderAllTechnoTriggers(graphics, previewMap, Globals.MapTileSize, Layers);
}
if ((Layers & MapLayerFlag.EffectRadius) == MapLayerFlag.EffectRadius)
{

View File

@ -281,7 +281,7 @@ namespace MobiusEditor.Tools
if ((layersToRender & MapLayerFlag.CellTriggers) == MapLayerFlag.CellTriggers
&& (manuallyHandledLayers & MapLayerFlag.CellTriggers) == MapLayerFlag.None)
{
MapRenderer.RenderCellTriggersSoft(graphics, map, tileSize, tileScale);
MapRenderer.RenderCellTriggersSoft(graphics, map, tileSize);
}
if ((layersToRender & (MapLayerFlag.Waypoints | MapLayerFlag.FootballArea)) == (MapLayerFlag.Waypoints | MapLayerFlag.FootballArea)
&& (manuallyHandledLayers & MapLayerFlag.WaypointsIndic) == MapLayerFlag.None && plugin.GameType == GameType.SoleSurvivor)
@ -307,22 +307,22 @@ namespace MobiusEditor.Tools
if ((layersToRender & (MapLayerFlag.Waypoints | MapLayerFlag.WaypointsIndic)) == (MapLayerFlag.Waypoints | MapLayerFlag.WaypointsIndic)
&& (manuallyHandledLayers & MapLayerFlag.WaypointsIndic) == MapLayerFlag.None)
{
MapRenderer.RenderWayPointIndicators(graphics, map, tileSize, tileScale, Color.LightGreen, false, true);
MapRenderer.RenderWayPointIndicators(graphics, map, tileSize, Color.LightGreen, false, true);
}
if ((layersToRender & (MapLayerFlag.Buildings | MapLayerFlag.BuildingFakes)) == (MapLayerFlag.Buildings | MapLayerFlag.BuildingFakes)
&& (manuallyHandledLayers & MapLayerFlag.BuildingFakes) == MapLayerFlag.None)
{
MapRenderer.RenderAllFakeBuildingLabels(graphics, map, tileSize, tileScale);
MapRenderer.RenderAllFakeBuildingLabels(graphics, map, tileSize);
}
if ((layersToRender & (MapLayerFlag.Buildings | MapLayerFlag.BuildingRebuild)) == (MapLayerFlag.Buildings | MapLayerFlag.BuildingRebuild)
&& (manuallyHandledLayers & MapLayerFlag.BuildingRebuild) == MapLayerFlag.None)
{
MapRenderer.RenderAllRebuildPriorityLabels(graphics, map, tileSize, tileScale);
MapRenderer.RenderAllRebuildPriorityLabels(graphics, map, tileSize);
}
if ((layersToRender & MapLayerFlag.TechnoTriggers) == MapLayerFlag.TechnoTriggers
&& (manuallyHandledLayers & MapLayerFlag.TechnoTriggers) == MapLayerFlag.None)
{
MapRenderer.RenderAllTechnoTriggers(graphics, map, tileSize, tileScale, layersToRender);
MapRenderer.RenderAllTechnoTriggers(graphics, map, tileSize, layersToRender);
}
}

View File

@ -467,13 +467,13 @@ namespace MobiusEditor.Tools
MapRenderer.RenderWaypoint(plugin.GameType, true, Globals.MapTileSize, map.FlagColors.ToArray(), selected, 1.0f).Item2(graphics);
}
// Render those here to they are put over the opaque redraw of the current waypoint.
MapRenderer.RenderAllTechnoTriggers(graphics, plugin.Map, Globals.MapTileSize, Globals.MapTileScale, Layers);
MapRenderer.RenderAllTechnoTriggers(graphics, plugin.Map, Globals.MapTileSize, Layers);
MapRenderer.RenderAllBoundsFromCell(graphics, Globals.MapTileSize,
map.Waypoints.Where(wp => wp != selected && wp.Cell.HasValue).Select(wp => wp.Cell.Value), map.Metrics, Color.Orange);
// For TD, always render reveal waypoint.
bool renderAll = plugin.GameType != GameType.RedAlert || (Layers & MapLayerFlag.WaypointRadius) == MapLayerFlag.WaypointRadius;
MapRenderer.RenderAllWayPointRevealRadiuses(graphics, plugin, map, Globals.MapTileSize, selected, !renderAll);
MapRenderer.RenderWayPointIndicators(graphics, map, Globals.MapTileSize, Globals.MapTileScale, Color.LightGreen, false, true, selectedRange);
MapRenderer.RenderWayPointIndicators(graphics, map, Globals.MapTileSize, Color.LightGreen, false, true, selectedRange);
if (selected != null)
{
if (placementMode && selectedIndex >= 0)
@ -484,11 +484,11 @@ namespace MobiusEditor.Tools
if (selected.Cell.HasValue)
{
MapRenderer.RenderAllBoundsFromCell(graphics, Globals.MapTileSize, new int[] { selected.Cell.Value }, map.Metrics, Color.Yellow);
MapRenderer.RenderWayPointIndicators(graphics, map, Globals.MapTileSize, Globals.MapTileScale, Color.Yellow, false, false, selectedRange);
MapRenderer.RenderWayPointIndicators(graphics, map, Globals.MapTileSize, Color.Yellow, false, false, selectedRange);
}
if (dummySelected != null && (selected == null || selected.Cell != dummySelected.Cell))
{
MapRenderer.RenderWayPointIndicators(graphics, map, Globals.MapTileSize, Globals.MapTileScale, Color.Yellow, true, false, new[] { dummySelected });
MapRenderer.RenderWayPointIndicators(graphics, map, Globals.MapTileSize, Color.Yellow, true, false, new[] { dummySelected });
// Need to do this manually since it's an extra waypoint not normally on the list, and it uses the radius data of the original waypoint to place.
int[] wpReveal1 = plugin.GetRevealRadiusForWaypoints(map, false);
int[] wpReveal2 = plugin.GetRevealRadiusForWaypoints(map, true);

View File

@ -500,7 +500,8 @@ namespace MobiusEditor.Utility
Int32 hdrIconsPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x0C);
// Offset of start of palette data. Probably always 0.
Int32 hdrPalettesPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x10);
// Offset of remaps data? Always fixed value "0x0D1AFFFF", which makes no sense as ptr.
// Offset of remaps data. Dune II leftover of 4 bit to 8 bit translation tables.
// Always fixed value 0x0D1AFFFF, which makes no sense as ptr.
Int32 hdrRemapsPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x14);
// Offset of 'transparency flags'? Generally points to an empty array at the end of the file.
Int32 hdrTransFlagPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x18);
@ -513,7 +514,85 @@ namespace MobiusEditor.Utility
if (hdrHeight != 24 || hdrWidth != 24)
throw new ArgumentException("Only 24×24 pixel tiles are supported.", "fileData");
// Checking some normally hardcoded values
if (hdrAllocated != 00 || hdrPalettesPtr != 0 || hdrRemapsPtr != 0x0D1AFFFF)
if (hdrAllocated != 0 || hdrPalettesPtr != 0)// || hdrRemapsPtr != 0x0D1AFFFF)
throw new ArgumentException("Invalid values encountered in header.");
if (hdrCount == 0)
throw new ArgumentException("Tileset files with 0 tiles are not supported!", "fileData");
// Checking if data is all inside the file
if (hdrIconsPtr >= fileLen || (hdrMapPtr + hdrCount) > fileLen)
throw new ArgumentException("Invalid header values: indices outside file range.", "fileData");
Int32 tileSize = hdrWidth * hdrHeight;
// Maps the available images onto the full iconset definition
Byte[] map = new Byte[hdrCount];
Array.Copy(fileData, hdrMapPtr, map, 0, hdrCount);
// Get max index plus one for real images count. Nothing in the file header actually specifies this directly.
Int32 actualImages = map.Max(x => x == 0xFF ? -1 : x) + 1;
if (hdrTransFlagPtr + actualImages > fileLen)
throw new ArgumentException("Invalid header values: indices outside file range.", "fileData");
if (hdrIconsPtr + actualImages * tileSize > fileLen)
throw new ArgumentException("Tile image data outside file range.", "fileData");
Byte[] imagesIndex = new Byte[actualImages];
Array.Copy(fileData, hdrTransFlagPtr, imagesIndex, 0, actualImages);
Byte[][] tiles = new Byte[hdrCount][];
widths = new int[hdrCount];
heights = new int[hdrCount];
Boolean[] tileUseList = new Boolean[map.Length];
for (Int32 i = 0; i < map.Length; ++i)
{
Byte dataIndex = map[i];
Boolean used = dataIndex != 0xFF;
tileUseList[i] = used;
Byte[] tileData = new Byte[tileSize];
if (used)
{
Int32 offset = hdrIconsPtr + dataIndex * tileSize;
if ((offset + tileSize) > fileLen)
throw new ArgumentException("Tile data outside file range.", "fileData");
Array.Copy(fileData, offset, tileData, 0, tileSize);
tiles[i] = tileData;
widths[i] = hdrWidth;
heights[i] = hdrHeight;
}
}
return tiles;
}
public static Byte[][] GetRaTmpData(Byte[] fileData, out int[] widths, out int[] heights)
{
Int32 fileLen = fileData.Length;
if (fileLen < 0x28)
throw new ArgumentException("File is not long enough to be a C&C Template file.", "fileData");
Int16 hdrWidth = ArrayUtils.ReadInt16FromByteArrayLe(fileData, 0x00);
Int16 hdrHeight = ArrayUtils.ReadInt16FromByteArrayLe(fileData, 0x02);
// Amount of icons to form the full icon set. Not necessarily the same as the amount of actual icons.
Int16 hdrCount = ArrayUtils.ReadInt16FromByteArrayLe(fileData, 0x04);
// Always 0
Int16 hdrAllocated = ArrayUtils.ReadInt16FromByteArrayLe(fileData, 0x06);
// New in RA
Int16 hdrMapWidth = ArrayUtils.ReadInt16FromByteArrayLe(fileData, 0x08);
Int16 hdrMapHeight = ArrayUtils.ReadInt16FromByteArrayLe(fileData, 0x0A);
Int32 hdrSize = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x0C);
// Offset of start of actual icon data. Generally always 0x20
Int32 hdrIconsPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x10);
// Offset of start of palette data. Probably always 0.
Int32 hdrPalettesPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x14);
// Offset of remaps data. Dune II leftover of 4 bit to 8 bit translation tables.
// Always seems to be 0x2C730FXX (with values differing for the lowest byte), which makes no sense as ptr.
Int32 hdrRemapsPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x18);
// Offset of 'transparency flags'? Generally points to an empty array at the end of the file.
Int32 hdrTransFlagPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x1C);
// Offset of 'color' map, indicating the terrain type for each type. This includes unused cells, which are usually indicated as 0.
Int32 hdrColorMapPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x20);
// Offset of actual icon set definition, defining for each index which icon data to use. FF for none.
Int32 hdrMapPtr = ArrayUtils.ReadInt32FromByteArrayLe(fileData, 0x24);
// File size check
if (hdrSize != fileData.Length)
throw new ArgumentException("File size in header does not match.", "fileData");
// Only allowing standard 24x24 size
if (hdrHeight != 24 || hdrWidth != 24)
throw new ArgumentException("Only 24×24 pixel tiles are supported.", "fileData");
// Checking some normally hardcoded values
if (hdrAllocated != 00 || hdrPalettesPtr != 0)
throw new ArgumentException("Invalid values encountered in header.");
if (hdrCount == 0)
throw new ArgumentException("Tileset files with 0 tiles are not supported!", "fileData");
@ -556,14 +635,6 @@ namespace MobiusEditor.Utility
return tiles;
}
public static Byte[][] GetRaTmpData(Byte[] fileData, out int width, out int height)
{
width = 24;
height = 24;
// TODO
return null;
}
public static Color[] LoadSixBitPalette(Byte[] fileData, int palStart, int colors)
{
Color[] palette = Enumerable.Repeat(Color.Black, colors).ToArray();

View File

@ -278,6 +278,45 @@ namespace MobiusEditor.Utility
return GeneralUtils.GetBoundingBoxCenter(image.Width, image.Height, maxWidth, maxHeight);
}
public static void RemoveAlphaOnCurrent(this Bitmap bitmap)
{
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
if ((bitmap.PixelFormat & PixelFormat.Indexed) == PixelFormat.Indexed)
{
ColorPalette pal = bitmap.Palette;
for (int i = 0; i < pal.Entries.Length; i++)
{
pal.Entries[i] = Color.FromArgb(255, pal.Entries[i]);
}
return;
}
if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
{
// can't handle.
return;
}
BitmapData sourceData = bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
Int32 actualDataWidth = rect.Width * 4;
Int32 h = bitmap.Height;
Int32 origStride = sourceData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
Int64 sourcePos = sourceData.Scan0.ToInt64();
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; ++y)
{
Marshal.Copy(new IntPtr(sourcePos), imageData, 0, actualDataWidth);
for (int i = 3; i < actualDataWidth; i += 4)
{
// Clear alpha
imageData[i] = 255;
}
Marshal.Copy(imageData, 0, new IntPtr(sourcePos), actualDataWidth);
sourcePos += origStride;
}
bitmap.UnlockBits(sourceData);
return;
}
public static Bitmap RemoveAlpha(this Bitmap bitmap)
{
Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
@ -285,7 +324,7 @@ namespace MobiusEditor.Utility
targetImage.SetResolution(bitmap.HorizontalResolution, bitmap.VerticalResolution);
BitmapData sourceData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(bitmap.PixelFormat) * rect.Width) + 7) / 8;
Int32 actualDataWidth = ((Image.GetPixelFormatSize(PixelFormat.Format32bppArgb) * rect.Width) + 7) / 8;
Int32 h = bitmap.Height;
Int32 origStride = sourceData.Stride;
Int32 targetStride = targetData.Stride;

View File

@ -16,7 +16,7 @@ namespace MobiusEditor.Utility
private bool isEmbedded = false;
private long fileStart;
private long fileLength;
private long dataStart;
private uint dataStart;
public Mixfile(string mixPath)
{
@ -42,18 +42,18 @@ namespace MobiusEditor.Utility
this.fileLength = length;
// Copy reference to parent map. The "CreateViewStream" function takes care of reading the right parts from it.
this.mixFileMap = container.mixFileMap;
this.ReadMixHeader(mixFileMap, offset, fileLength);
this.ReadMixHeader(this.mixFileMap, offset, this.fileLength);
}
private void ReadMixHeader(MemoryMappedFile mixMap, long mixStart, long mixLength)
{
mixFileContents.Clear();
this.mixFileContents.Clear();
uint readOffset = 0;
ushort nrOfFiles = 0;
bool hasFlags = false;
bool encrypted = false;
bool checksum = false;
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
using (BinaryReader headerReader = new BinaryReader(this.CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
{
ushort start = headerReader.ReadUInt16();
if (start == 0)
@ -64,9 +64,9 @@ namespace MobiusEditor.Utility
}
if (hasFlags)
{
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
using (BinaryReader headerReader = new BinaryReader(this.CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
{
var flags = headerReader.ReadUInt16();
ushort flags = headerReader.ReadUInt16();
checksum = (flags & 1) != 0;
encrypted = (flags & 2) != 0;
readOffset += 2;
@ -74,7 +74,7 @@ namespace MobiusEditor.Utility
// Not encrypted; read nr of files.
if (!encrypted)
{
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
using (BinaryReader headerReader = new BinaryReader(this.CreateViewStream(mixMap, mixStart, mixLength, readOffset, 2)))
{
nrOfFiles = headerReader.ReadUInt16();
readOffset += 2;
@ -85,7 +85,7 @@ namespace MobiusEditor.Utility
Byte[] header = null;
if (encrypted)
{
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, 80)))
using (BinaryReader headerReader = new BinaryReader(this.CreateViewStream(mixMap, mixStart, mixLength, readOffset, 80)))
{
byte[] blowfishKey = headerReader.ReadAllBytes();
readOffset += 80;
@ -102,7 +102,7 @@ namespace MobiusEditor.Utility
{
throw new ArgumentOutOfRangeException("Not a valid mix file: header length exceeds file length.");
}
using (BinaryReader headerReader = new BinaryReader(CreateViewStream(mixMap, mixStart, mixLength, readOffset, headerSize)))
using (BinaryReader headerReader = new BinaryReader(this.CreateViewStream(mixMap, mixStart, mixLength, readOffset, headerSize)))
{
header = headerReader.ReadBytes((Int32)headerSize);
// End of header reading; no longer needed.
@ -123,7 +123,7 @@ namespace MobiusEditor.Utility
{
throw new ArgumentOutOfRangeException(String.Format("Not a valid mix file: file #{0} with id {1:X08} exceeds archive length.", i, fileId));
}
mixFileContents.Add(fileId, (fileOffset, fileLength));
this.mixFileContents.Add(fileId, (fileOffset, fileLength));
}
}
}
@ -132,26 +132,24 @@ namespace MobiusEditor.Utility
{
offset = 0;
length = 0;
uint fileId = hashRol.GetNameId(filename);
uint fileId = this.hashRol.GetNameId(filename);
(uint Offset, uint Length) fileLoc;
if (!mixFileContents.TryGetValue(fileId, out fileLoc))
if (!this.mixFileContents.TryGetValue(fileId, out fileLoc))
{
return false;
}
offset = fileLoc.Offset;
offset = fileLoc.Offset + this.dataStart;
length = fileLoc.Length;
return true;
}
public Stream OpenFile(string path)
{
uint fileId = hashRol.GetNameId(path);
(uint Offset, uint Length) fileLoc;
if (!mixFileContents.TryGetValue(fileId, out fileLoc))
if (!this.GetFileInfo(path, out uint offset, out uint length))
{
return null;
}
return CreateViewStream(mixFileMap, fileStart, fileLength, this.dataStart + fileLoc.Offset, fileLoc.Length);
return this.CreateViewStream(this.mixFileMap, this.fileStart, this.fileLength, offset, length);
}
/// <summary>
@ -166,7 +164,7 @@ namespace MobiusEditor.Utility
/// <exception cref="IndexOutOfRangeException">The data is not in the bounds of this mix file.</exception>
private Stream CreateViewStream(MemoryMappedFile mixMap, long mixFileStart, long mixFileLength, long dataReadOffset, uint dataReadLength)
{
if (disposedValue)
if (this.disposedValue)
{
throw new ObjectDisposedException("Mixfile");
}
@ -182,22 +180,22 @@ namespace MobiusEditor.Utility
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
if (!this.disposedValue)
{
// Only dispose if not an embedded mix file.
// If embedded, the mixFileMap is contained in the parent.
if (disposing && !isEmbedded)
if (disposing && !this.isEmbedded)
{
mixFileMap.Dispose();
this.mixFileMap.Dispose();
}
mixFileMap = null;
disposedValue = true;
this.mixFileMap = null;
this.disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
this.Dispose(true);
}
#endregion
}

View File

@ -40,7 +40,7 @@ namespace MobiusEditor.Utility
this.Name = name;
this.UnitRadarColor = unitRadarColor;
this.BuildingRadarColor = buildingRadarColor;
this.remapTable = Enumerable.Range(0, 0x100).Select(b => (Byte)b).ToArray();
this.remapTable = Enumerable.Range(0, 0x100).Select(b => (byte)b).ToArray();
int max = Math.Min(0x100, remapstart + remapValues.Length) - remapstart;
for (int i = 0; i < max; ++i)
{
@ -53,7 +53,7 @@ namespace MobiusEditor.Utility
this.Name = name;
this.UnitRadarColor = unitRadarColor;
this.BuildingRadarColor = buildingRadarColor;
this.remapTable = Enumerable.Range(0, 0x100).Cast<byte>().ToArray();
this.remapTable = Enumerable.Range(0, 0x100).Select(b => (byte)b).ToArray();
int max = Math.Max(remapOrigins.Length, remapValues.Length);
for (int i = 0; i < max; ++i)
{
@ -100,6 +100,7 @@ namespace MobiusEditor.Utility
}
}
}
public void ApplyToImage(byte[] bytes, int width, int height, int bytesPerPixel, int stride, Rectangle? opaqueBounds)
{
// Only handle 8bpp data.
@ -110,7 +111,7 @@ namespace MobiusEditor.Utility
Rectangle bounds = opaqueBounds ?? new Rectangle(0, 0, width, height);
int boundsBottom = Math.Min(height, bounds.Bottom);
int boundsWidth = Math.Min(Math.Max(0, width - bounds.Left), bounds.Width);
int linePtr = 0;
int linePtr = bounds.Top * stride;
for (int y = bounds.Top; y < boundsBottom; y++)
{
int ptr = linePtr + bounds.Left;

View File

@ -32,7 +32,7 @@ namespace MobiusEditor.Utility
static TeamRemapManager()
{
RemapsTd = (from field in typeof(ITeamColorManager).GetFields(BindingFlags.Static | BindingFlags.Public)
RemapsTd = (from field in typeof(TeamRemapManager).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);
}

View File

@ -139,7 +139,7 @@ namespace MobiusEditor.Utility
{
if (generateFallback && tiles.Any(t => t.Image == null))
{
// Tile found, but contains no data. Re-fetch with dummy generation.
// Tile not found, or contains no data. Re-fetch with dummy generation.
if (tileset.GetTileData(name, shape, teamColor, out fps, out tiles, true))
{
// Signal in return value that dummy was generated.
@ -151,7 +151,7 @@ namespace MobiusEditor.Utility
}
}
// If the tile is not defined at all, and onlyifdefined is not enabled, make a dummy entry anyway.
if (!onlyIfDefined && generateFallback && first != null && first.GetTileData(name, shape, teamColor, out fps, out tiles, true))
if (generateFallback && !onlyIfDefined && first != null && first.GetTileData(name, shape, teamColor, out fps, out tiles, true))
{
// Signal in return value that dummy was generated.
return false;

View File

@ -60,7 +60,7 @@ namespace MobiusEditor.Utility
this.currentlyLoadedPalette = TeamRemapManager.GetPaletteForTheater(this.archiveManager, theater);
}
public Boolean GetTeamColorTileData(String name, Int32 shape, ITeamColor teamColor, out Tile tile, Boolean generateFallback, Boolean onlyIfDefined)
public Boolean GetTeamColorTileData(String name, Int32 shape, ITeamColor teamColor, out Tile tile, Boolean generateFallback, Boolean onlyIfDefined, string remapGraphicsSource, byte[] remapTable)
{
tile = null;
String teamColorName = teamColor == null ? String.Empty : (teamColor.Name ?? String.Empty);
@ -78,7 +78,8 @@ namespace MobiusEditor.Utility
}
else
{
shapeFile = this.GetShapeFile(name);
// If there's a remap graphics source, prefer that.
shapeFile = this.GetShapeFile(remapGraphicsSource ?? name);
if (shapeFile == null)
{
if (!generateFallback)
@ -88,25 +89,44 @@ namespace MobiusEditor.Utility
shapeFile = new Dictionary<int, ShapeFrameData>();
}
tileData[name] = shapeFile;
// System to fix RA's remapped infantry. Since everything is cached, this only works if the very
// first call to fetch these graphics is guaranteed to pass along the graphics source and remap info.
if (remapTable != null && remapTable.Length >= 0x100)
{
foreach (int key in shapeFile.Keys)
{
ShapeFrameData sfd = shapeFile[key];
Byte[] frameGfx = sfd.FrameData;
for (int i = 0; i < frameGfx.Length; ++i)
{
frameGfx[i] = remapTable[frameGfx[i]];
}
}
}
// Remaps the tile, and takes care of caching it and possibly generating dummies.
tile = this.RemapShapeFile(shapeFile, shape, teamColor, generateFallback);
}
return tile != null;
}
public Boolean GetTeamColorTileData(String name, Int32 shape, ITeamColor teamColor, out Tile tile)
public Boolean GetTeamColorTileData(String name, Int32 shape, ITeamColor teamColor, out Tile tile, Boolean generateFallback, Boolean onlyIfDefined)
{
return GetTeamColorTileData(name, shape, teamColor, out tile, false, false);
return GetTeamColorTileData(name, shape, teamColor, out tile, generateFallback, onlyIfDefined, null, null);
}
public Boolean GetTileData(String name, Int32 shape, out Tile tile, Boolean generateFallback, Boolean onlyIfDefined)
{
return GetTeamColorTileData(name, shape, null, out tile, false, false);
return GetTeamColorTileData(name, shape, null, out tile, generateFallback, onlyIfDefined, null, null);
}
public Boolean GetTeamColorTileData(String name, Int32 shape, ITeamColor teamColor, out Tile tile)
{
return GetTeamColorTileData(name, shape, teamColor, out tile, false, false, null, null);
}
public Boolean GetTileData(String name, Int32 shape, out Tile tile)
{
return GetTeamColorTileData(name, shape, null, out tile, false, false);
return GetTeamColorTileData(name, shape, null, out tile, false, false, null, null);
}
public int GetTileDataLength(string name)
@ -117,7 +137,13 @@ namespace MobiusEditor.Utility
}
if (!this.tileData.TryGetValue(name, out Dictionary<int, ShapeFrameData> shapes))
{
return -1;
// If it's not cached yet, fetch without caching. This avoids issues with the special remap system.
// These ShapeFrameData objects don't need to be disposed since they can't contain Bitmap objects yet.
shapes = GetShapeFile(name);
if (shapes == null)
{
return -1;
}
}
return shapes.Max(kv => kv.Key) + 1;
}
@ -125,8 +151,22 @@ namespace MobiusEditor.Utility
private Dictionary<int, ShapeFrameData> GetShapeFile(String name)
{
bool isShpExt = false;
Byte[] fileContents = null;
// If it has an extension, force it.
if (Path.HasExtension(name))
{
fileContents = GetFileContents(name);
// Immediately abort; classic file system does not support double extensions.
if (fileContents == null)
{
return null;
}
}
// Try theater extension, then ".shp".
Byte[] fileContents = GetFileContents(name + "." + theater.ClassicExtension);
if (fileContents == null)
{
fileContents = GetFileContents(name + "." + theater.ClassicExtension);
}
if (fileContents == null)
{
isShpExt = true;
@ -168,15 +208,7 @@ namespace MobiusEditor.Utility
try
{
// RA map template tileset
int width;
int height;
shpData = ClassicSpriteLoader.GetRaTmpData(fileContents, out width, out height);
if (shpData != null)
{
int len = shpData.Length;
widths = Enumerable.Repeat(width, len).ToArray();
heights = Enumerable.Repeat(height, len).ToArray();
}
shpData = ClassicSpriteLoader.GetRaTmpData(fileContents, out widths, out heights);
}
catch (ArgumentException) { /* ignore */ }
}
@ -228,10 +260,11 @@ namespace MobiusEditor.Utility
return null;
}
// Make average-sized dummy.
int minWidth = shapeFile.Values.Where(v => v.Width != 0).Min(v => v.Width);
int maxWidth = shapeFile.Values.Where(v => v.Width != 0).Max(v => v.Width);
int minHeight = shapeFile.Values.Where(v => v.Height != 0).Min(v => v.Height);
int maxHeight = shapeFile.Values.Where(v => v.Height != 0).Max(v => v.Height);
bool noValues = shapeFile.Values.Count == 0;
int minWidth = noValues? 24 : shapeFile.Values.Where(v => v.Width != 0).Min(v => v.Width);
int maxWidth = noValues? 24 : shapeFile.Values.Where(v => v.Width != 0).Max(v => v.Width);
int minHeight = noValues? 24 : shapeFile.Values.Where(v => v.Height != 0).Min(v => v.Height);
int maxHeight = noValues? 24 : shapeFile.Values.Where(v => v.Height != 0).Max(v => v.Height);
int dummyWidth = minWidth + (maxWidth - minWidth) / 2;
int dummyHeight = minHeight + (maxHeight - minHeight) / 2;
frameData = GenerateDummy(dummyWidth, dummyHeight);
@ -249,9 +282,20 @@ namespace MobiusEditor.Utility
if (teamColor != null && !String.IsNullOrEmpty(teamColorName) && !frameData.IsDummy)
{
// Finally, the actual remapping!
teamColor.ApplyToImage(data, width, height, 1, width, opaqueBounds);
byte[] dataRemap = new byte[data.Length];
Array.Copy(data, 0, dataRemap, 0, data.Length);
teamColor.ApplyToImage(dataRemap, width, height, 1, width, opaqueBounds);
data = dataRemap;
}
Bitmap bm = ImageUtils.BuildImage(data, width, height, width, PixelFormat.Format8bppIndexed, currentlyLoadedPalette, null);
Color[] pal = currentlyLoadedPalette;
if (frameData.IsDummy)
{
// Make gray colour semitransparent on dummy graphics.
pal = new Color[currentlyLoadedPalette.Length];
Array.Copy(currentlyLoadedPalette, 0, pal, 0, pal.Length);
pal[14] = Color.FromArgb(0x80, pal[14]);
}
Bitmap bm = ImageUtils.BuildImage(data, width, height, width, PixelFormat.Format8bppIndexed, pal, null);
tile = new Tile(bm, opaqueBounds);
frameData.TeamColorTiles.Add(teamColorName, tile);
return tile;