Added classic font rendering for techno triggers.

* Fixed overlap detection of walls in both TD and RA; they can only be obstructed by buildings now.
* Fixed bug where editing properties of an object would clear its trigger in the dropdown.
* Replaced usages of "6point.fnt" in RA by "editfnt.fnt", because it is constant, while "6point.fnt" differs in lores.mix and hires.mix.
This commit is contained in:
Nyerguds 2024-08-12 16:18:21 +02:00
parent 2ff36fbb7c
commit d14a46cb79
8 changed files with 108 additions and 75 deletions

View File

@ -151,7 +151,6 @@ namespace MobiusEditor.Controls
captureUnknownImage = ToolStripRenderer.CreateDisabledImage(captureImage);
houseComboBox.DataSource = plugin.Map.Houses.Select(t => new TypeItem<HouseType>(t.Type.Name, t.Type)).ToArray();
missionComboBox.DataSource = plugin.Map.MissionTypes;
UpdateDataSource();
Disposed += (sender, e) =>
{
Object = null;
@ -166,14 +165,22 @@ namespace MobiusEditor.Controls
private void UpdateDataSource()
{
if (obj == null)
{
return;
}
string selected = triggerComboBox.SelectedItem as string;
triggerComboBox.DataBindings.Clear();
triggerComboBox.SelectedIndexChanged -= this.TriggerComboBox_SelectedIndexChanged;
triggerComboBox.DataSource = null;
triggerComboBox.Items.Clear();
string[] items;
Boolean isAircraft = obj is Unit un && un.Type.IsAircraft;
Boolean isOnMap = true;
bool isAircraft = obj is Unit un && un.Type.IsAircraft;
bool isOnMap = true;
if (selected == null && obj is ITechno tch)
{
selected = tch.Trigger;
}
switch (obj)
{
case Infantry infantry:
@ -206,7 +213,6 @@ namespace MobiusEditor.Controls
int sel = triggerComboBox.SelectedIndex;
triggerComboBox.SelectedIndexChanged += this.TriggerComboBox_SelectedIndexChanged;
triggerComboBox.SelectedItem = items[selectIndex];
TriggerComboBox_SelectedIndexChanged(triggerComboBox, new EventArgs());
if (sel == selectIndex)
{
TriggerComboBox_SelectedIndexChanged(triggerComboBox, new EventArgs());

View File

@ -12,7 +12,8 @@
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. namespace MobiusEditor
// along with this program. If not, see <https://www.gnu.org/licenses/>.
namespace MobiusEditor
{
partial class GameInstallationPathForm
{

View File

@ -260,16 +260,16 @@ namespace MobiusEditor.Model
public enum ClassicFont
{
/// <summary>Font used for Waypoints</summary>
/// <summary>Font used for Waypoints.</summary>
Waypoints,
/// <summary>Font used for waypoints with longer names. Separate because it needs a smaller font to fit inside one cell.</summary>
WaypointsLong,
/// <summary>Font used for cell triggers</summary>
/// <summary>Font used for cell triggers.</summary>
CellTriggers,
/// <summary>Font used for techno triggers, except infantry</summary>
/// <summary>Font used for techno triggers on multi-cell objects.</summary>
TechnoTriggers,
/// <summary>Font used for infantry techno triggers. Separate because it might need to be smaller.</summary>
InfantryTriggers,
/// <summary>Font used for one-cell techno triggers. Separate because it might need to be smaller.</summary>
TechnoTriggersSmall,
/// <summary>Font used for rebuild priority numbers on buildings.</summary>
RebuildPriority,
/// <summary>Font used for "FAKE" labels on buildings.</summary>

View File

@ -252,7 +252,7 @@ namespace MobiusEditor.RedAlert
break;
case ClassicFont.WaypointsLong:
crop = true;
fontName = "6point.fnt";
fontName = "editfnt.fnt";
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor, 2, 3);
break;
case ClassicFont.CellTriggers:
@ -266,14 +266,18 @@ namespace MobiusEditor.RedAlert
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor);
break;
case ClassicFont.TechnoTriggers:
case ClassicFont.InfantryTriggers:
crop = true;
fontName = "editfnt.fnt";
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor, 2, 3);
break;
case ClassicFont.TechnoTriggersSmall:
crop = true;
fontName = "3point.fnt";
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor);
break;
case ClassicFont.FakeLabels:
crop = true;
fontName = "6point.fnt";
fontName = "editfnt.fnt";
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor, 2, 3);
break;
}

View File

@ -2476,7 +2476,7 @@ namespace MobiusEditor.RedAlert
modified = true;
continue;
}
if ((overlayType.IsWall || overlayType.IsSolid) && Map.Technos.ObjectAt(i, out ICellOccupier techno))
if ((overlayType.IsWall || overlayType.IsSolid) && Map.Buildings.ObjectAt(i, out ICellOccupier techno))
{
string desc = overlayType.IsWall ? "Wall" : "Solid overlay";
if (techno is Building building)
@ -2484,21 +2484,6 @@ namespace MobiusEditor.RedAlert
errors.Add(string.Format("{0} '{1}' overlaps structure '{2}' at cell {3}; skipping.", desc, overlayType.Name, building.Type.Name, i));
modified = true;
}
else if (techno is Terrain terrain)
{
errors.Add(string.Format("{0} '{1}' overlaps terrain '{2}' at cell {3}; skipping.", desc, overlayType.Name, terrain.Type.Name, i));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("{0} '{1}' overlaps unit '{2}' at cell {3}; skipping.", desc, overlayType.Name, unit.Type.Name, i));
modified = true;
}
else if (techno is InfantryGroup)
{
errors.Add(string.Format("{0} '{1}' overlaps infantry at cell {2}; skipping.", desc, overlayType.Name, i));
modified = true;
}
else
{
errors.Add(string.Format("{0} '{1}' overlaps unknown techno in cell {2}; skipping.", desc, overlayType.Name, i));

View File

@ -2025,16 +2025,16 @@ namespace MobiusEditor.Render
public static void RenderAllTechnoTriggers(Graphics graphics, GameInfo gameInfo, OccupierSet<ICellOccupier> mapTechnos, OccupierSet<ICellOccupier> mapBuildings, Rectangle visibleCells, Size tileSize, MapLayerFlag layersToRender, Color color, string toPick, bool excludePick)
{
string classicFont = null;
bool cropClassicFont = false;
string classicFontInf = null;
bool cropClassicFontInf = false;
TeamRemap remapClassicFont = null;
TeamRemap remapClassicFontInf = null;
string classicFontLarge = null;
bool cropClassicFontLarge = false;
string classicFontSmall = null;
bool cropClassicFontSmall = false;
TeamRemap remapClassicFontLarge = null;
TeamRemap remapClassicFontSmall = null;
if (Globals.TheTilesetManager is TilesetManagerClassic tsmc && Globals.TheTeamColorManager is TeamRemapManager trm)
{
classicFont = gameInfo.GetClassicFontInfo(ClassicFont.TechnoTriggers, tsmc, trm, color, out cropClassicFont, out remapClassicFont);
classicFontInf = gameInfo.GetClassicFontInfo(ClassicFont.InfantryTriggers, tsmc, trm, color, out cropClassicFontInf, out remapClassicFontInf);
classicFontLarge = gameInfo.GetClassicFontInfo(ClassicFont.TechnoTriggers, tsmc, trm, color, out cropClassicFontLarge, out remapClassicFontLarge);
classicFontSmall = gameInfo.GetClassicFontInfo(ClassicFont.TechnoTriggersSmall, tsmc, trm, color, out cropClassicFontSmall, out remapClassicFontSmall);
}
double tileScaleHor = tileSize.Width / 128.0;
float borderSize = Math.Max(0.5f, tileSize.Width / 60.0f);
@ -2051,8 +2051,9 @@ namespace MobiusEditor.Render
{
if (visibleCells.IntersectsWith(new Rectangle(topLeft, terrain.Type.Size)))
{
Size size = new Size(terrain.Type.Size.Width * tileSize.Width, terrain.Type.Size.Height * tileSize.Height);
triggers = new (string, Rectangle, int)[] { (terrain.Trigger, new Rectangle(location, size), terrain.IsPreview ? Globals.PreviewAlphaInt : 255) };
Size size = new Size(terrain.Type.Size.Width * Globals.OriginalTileWidth, terrain.Type.Size.Height * Globals.OriginalTileHeight);
triggers = new (string, Rectangle, int)[] { (terrain.Trigger, new Rectangle(location, size),
terrain.IsPreview ? Globals.PreviewAlphaInt : 256) };
}
}
}
@ -2062,7 +2063,8 @@ namespace MobiusEditor.Render
{
if (visibleCells.Contains(topLeft))
{
triggers = new (string, Rectangle, int)[] { (unit.Trigger, new Rectangle(location, tileSize), unit.IsPreview ? Globals.PreviewAlphaInt : 255) };
triggers = new (string, Rectangle, int)[] { (unit.Trigger, new Rectangle(location, Globals.OriginalTileSize),
unit.IsPreview ? Globals.PreviewAlphaInt : 256) };
}
}
}
@ -2082,7 +2084,7 @@ namespace MobiusEditor.Render
{
continue;
}
Size size = tileSize;
Size size = Globals.OriginalTileSize;
Size offset = Size.Empty;
switch ((InfantryStoppingType)i)
{
@ -2104,7 +2106,7 @@ namespace MobiusEditor.Render
break;
}
Rectangle bounds = new Rectangle(location + offset, size);
infantryTriggers.Add((infantry.Trigger, bounds, infantry.IsPreview ? Globals.PreviewAlphaInt : 255));
infantryTriggers.Add((infantry.Trigger, bounds, infantry.IsPreview ? Globals.PreviewAlphaInt : 256));
}
triggers = infantryTriggers.ToArray();
}
@ -2126,8 +2128,9 @@ namespace MobiusEditor.Render
{
if (visibleCells.IntersectsWith(new Rectangle(topLeft, building.Type.Size)))
{
Size size = new Size(building.Type.Size.Width * tileSize.Width, building.Type.Size.Height * tileSize.Height);
allTriggers.Add((building.Trigger, new Rectangle(location, size), building.IsPreview ? Globals.PreviewAlphaInt : 255));
Size size = new Size(building.Type.Size.Width * Globals.OriginalTileWidth, building.Type.Size.Height * Globals.OriginalTileHeight);
allTriggers.Add((building.Trigger, new Rectangle(location, size),
building.IsPreview ? Globals.PreviewAlphaInt : 256));
}
}
}
@ -2142,19 +2145,56 @@ namespace MobiusEditor.Render
|| (excludePick && !x.trigger.Equals(toPick, StringComparison.OrdinalIgnoreCase))
|| (!excludePick && x.trigger.Equals(toPick, StringComparison.OrdinalIgnoreCase))))
{
Color alphaColor = Color.FromArgb(alpha, color);
using (SolidBrush technoTriggerBackgroundBrush = new SolidBrush(Color.FromArgb(96 * alpha / 256, Color.Black)))
using (SolidBrush technoTriggerBrush = new SolidBrush(alphaColor))
using (Pen technoTriggerPen = new Pen(alphaColor, borderSize))
using (Font font = graphics.GetAdjustedFont(trigger, SystemFonts.DefaultFont, bounds.Width, bounds.Height,
Math.Max(1, (int)Math.Round(12 * tileScaleHor)), Math.Max(1, (int)Math.Round(24 * tileScaleHor)), stringFormat, true))
// Larger than a single cell.
bool isLarge = bounds.Width > Globals.OriginalTileWidth;
string classicFont = isLarge ? classicFontLarge : classicFontSmall;
bool cropClassicFont = isLarge ? cropClassicFontLarge : cropClassicFontSmall;
TeamRemap remapClassicFont = isLarge ? remapClassicFontLarge : remapClassicFontSmall;
Color alphaColor = Color.FromArgb(alpha.Restrict(0,255), color);
if (classicFont == null)
{
SizeF textBounds = graphics.MeasureString(trigger, font, bounds.Width, stringFormat);
RectangleF backgroundBounds = new RectangleF(bounds.Location, textBounds);
backgroundBounds.Offset((bounds.Width - textBounds.Width) / 2.0f, (bounds.Height - textBounds.Height) / 2.0f);
graphics.FillRectangle(technoTriggerBackgroundBrush, backgroundBounds);
graphics.DrawRectangle(technoTriggerPen, Rectangle.Round(backgroundBounds));
graphics.DrawString(trigger, font, technoTriggerBrush, bounds, stringFormat);
int width = bounds.Width * tileSize.Width / Globals.OriginalTileWidth;
int height = bounds.Height * tileSize.Height / Globals.OriginalTileHeight;
Rectangle realBounds = new Rectangle(bounds.Location, new Size(width, height));
using (SolidBrush technoTriggerBackgroundBrush = new SolidBrush(Color.FromArgb((96 * alpha / 256).Restrict(0, 255), Color.Black)))
using (SolidBrush technoTriggerBrush = new SolidBrush(alphaColor))
using (Pen technoTriggerPen = new Pen(alphaColor, borderSize))
using (Font font = graphics.GetAdjustedFont(trigger, SystemFonts.DefaultFont, width, height,
Math.Max(1, (int)Math.Round(12 * tileScaleHor)), Math.Max(1, (int)Math.Round(24 * tileScaleHor)), stringFormat, true))
{
SizeF textBounds = graphics.MeasureString(trigger, font, width, stringFormat);
RectangleF backgroundBounds = new RectangleF(bounds.Location, textBounds);
backgroundBounds.Offset((width - textBounds.Width) / 2.0f, (height - textBounds.Height) / 2.0f);
graphics.FillRectangle(technoTriggerBackgroundBrush, backgroundBounds);
graphics.DrawRectangle(technoTriggerPen, Rectangle.Round(backgroundBounds));
graphics.DrawString(trigger, font, technoTriggerBrush, realBounds, stringFormat);
}
}
else
{
int[] indices = Encoding.ASCII.GetBytes(trigger).Select(x => (int)x).ToArray();
using (SolidBrush technoTriggerBackgroundBrush = new SolidBrush(Color.FromArgb(96, Color.Black)))
using (Pen technoTriggerPen = new Pen(color, 1))
using (Bitmap txt = RenderTextFromSprite(classicFont, remapClassicFont, Size.Empty, indices, false, cropClassicFont))
using (Bitmap txt2 = new Bitmap(txt.Width + 4, txt.Height + 4))
using (ImageAttributes imageAttributes = new ImageAttributes())
{
txt2.SetResolution(96, 96);
using (Graphics txt2g = Graphics.FromImage(txt2))
{
txt2g.FillRectangle(technoTriggerBackgroundBrush, new Rectangle(1, 1, txt2.Width - 2, txt2.Height - 2));
txt2g.DrawRectangle(technoTriggerPen, new Rectangle(0, 0, txt2.Width - 1, txt2.Height - 1));
txt2g.DrawImage(txt, new Rectangle(2, 2, txt.Width, txt.Height));
}
imageAttributes.SetColorMatrix(GetColorMatrix(Color.White, 1.0f, alpha / 256.0f));
int paintOffsX = (bounds.Width - txt2.Width) / 2 * tileSize.Width / Globals.OriginalTileWidth;
int paintOffsY = (bounds.Height - txt2.Height) / 2 * tileSize.Width / Globals.OriginalTileWidth;
int textWidth = txt2.Width * tileSize.Width / Globals.OriginalTileWidth;
int textHeight = txt2.Height * tileSize.Width / Globals.OriginalTileWidth;
Rectangle paintBounds = new Rectangle(bounds.Location, new Size(textWidth, textHeight));
paintBounds.Offset(new Point(paintOffsX, paintOffsY));
graphics.DrawImage(txt2, paintBounds, 0, 0, txt2.Width, txt2.Height, GraphicsUnit.Pixel, imageAttributes);
}
}
}
}
@ -2244,7 +2284,6 @@ namespace MobiusEditor.Render
Globals.TheShapeCacheManager.AddImage(wpId, wpBm);
Rectangle paintRect = new Rectangle(paintBounds.Location.X, paintBounds.Location.Y, wpBm.Width, wpBm.Height);
graphics.DrawImage(wpBm, paintRect, 0, 0, wpBm.Width, wpBm.Height, GraphicsUnit.Pixel, imageAttributes);
}
}
}
@ -2883,12 +2922,13 @@ namespace MobiusEditor.Render
}
else
{
// Solid overlays in the buildings list.
isBuilding = building != null;
}
bool isTechno = techno != null || isBuilding;
// Skip if it's the techno-loop and there's no techno,
// or if it's not the techno-loop and there is a techno (to avoid overlap).
if ((isTechno && !forTechnos) || (!isTechno && forTechnos))
// Skip if it's a techno-loop and there's no techno,
// or if it's not a techno-loop and there is a techno (to avoid overlap).
if ((forTechnos && !isTechno) || (!forTechnos && isTechno))
{
continue;
}
@ -3165,9 +3205,12 @@ namespace MobiusEditor.Render
int curWidth = 0;
if (lineLength == 0 || maxHeight - minTop == 0)
{
return new Bitmap(2, 2);
Bitmap bm = new Bitmap(2, 2);
bm.SetResolution(96, 96);
return bm;
}
Bitmap bitmap = new Bitmap(lineLength, maxHeight - minTop, PixelFormat.Format32bppArgb);
bitmap.SetResolution(96, 96);
using (Graphics g = Graphics.FromImage(bitmap))
{
for (int i = 0; i < nrOfChars; ++i)

View File

@ -179,7 +179,11 @@ namespace MobiusEditor.TiberianDawn
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor);
break;
case ClassicFont.TechnoTriggers:
case ClassicFont.InfantryTriggers:
crop = true;
fontName = "scorefnt.fnt";
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor);
break;
case ClassicFont.TechnoTriggersSmall:
crop = true;
fontName = "3point.fnt";
remap = GetClassicFontRemapSimple(fontName, tsmc, trm, textColor);

View File

@ -2117,7 +2117,7 @@ namespace MobiusEditor.TiberianDawn
modified = true;
continue;
}
if ((overlayType.IsWall || overlayType.IsSolid) && Map.Technos.ObjectAt(cell, out ICellOccupier techno))
if ((overlayType.IsWall || overlayType.IsSolid) && Map.Buildings.ObjectAt(cell, out ICellOccupier techno))
{
string desc = overlayType.IsWall ? "Wall" : "Solid overlay";
if (techno is Building building)
@ -2125,19 +2125,9 @@ namespace MobiusEditor.TiberianDawn
errors.Add(string.Format("{0} '{1}' overlaps structure '{2}' at cell {3}; skipping.", desc, overlayType.Name, building.Type.Name, cell));
modified = true;
}
else if (techno is Terrain terrain)
else if (techno is Overlay ovl)
{
errors.Add(string.Format("{0} '{1}' overlaps terrain '{2}' at cell {3}; skipping.", desc, overlayType.Name, terrain.Type.Name, cell));
modified = true;
}
else if (techno is Unit unit)
{
errors.Add(string.Format("{0} '{1}' overlaps unit '{2}' at cell {3}; skipping.", desc, overlayType.Name, unit.Type.Name, cell));
modified = true;
}
else if (techno is InfantryGroup)
{
errors.Add(string.Format("{0} '{1}' overlaps infantry at cell {2}; skipping.", desc, overlayType.Name, cell));
errors.Add(string.Format("{0} '{1}' overlaps overlay '{2}' at cell {3}; skipping.", desc, overlayType.Name, ovl.Type.Name, cell));
modified = true;
}
else