added function comments, cleaned up some obsolete code, added some tools necessary for classic support.
This commit is contained in:
parent
26a7854409
commit
02d1992055
@ -138,8 +138,8 @@ namespace MobiusEditor.Controls
|
|||||||
case Building building:
|
case Building building:
|
||||||
isOnMap = building.IsPrebuilt;
|
isOnMap = building.IsPrebuilt;
|
||||||
items = Plugin.Map.FilterStructureTriggers().Select(t => t.Name).Distinct().ToArray();
|
items = Plugin.Map.FilterStructureTriggers().Select(t => t.Name).Distinct().ToArray();
|
||||||
filteredEvents = Plugin.Map.EventTypes.Where(ac => Plugin.Map.StructureEventTypes.Contains(ac)).Distinct().ToArray();
|
filteredEvents = Plugin.Map.EventTypes.Where(ac => Plugin.Map.BuildingEventTypes.Contains(ac)).Distinct().ToArray();
|
||||||
filteredActions = Plugin.Map.ActionTypes.Where(ac => Plugin.Map.StructureActionTypes.Contains(ac)).Distinct().ToArray();
|
filteredActions = Plugin.Map.ActionTypes.Where(ac => Plugin.Map.BuildingActionTypes.Contains(ac)).Distinct().ToArray();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
items = Plugin.Map.Triggers.Select(t => t.Name).Distinct().ToArray();
|
items = Plugin.Map.Triggers.Select(t => t.Name).Distinct().ToArray();
|
||||||
|
@ -254,7 +254,8 @@ namespace MobiusEditor.Dialogs
|
|||||||
briefingSettingsTracker.TryGetMember("Briefing", out object brf);
|
briefingSettingsTracker.TryGetMember("Briefing", out object brf);
|
||||||
if (brf is String brief)
|
if (brf is String brief)
|
||||||
{
|
{
|
||||||
if (!plugin.EvaluateBriefing(brief, out String message) && !String.IsNullOrEmpty(message))
|
string message = plugin.EvaluateBriefing(brief);
|
||||||
|
if (message != null)
|
||||||
{
|
{
|
||||||
message += "\n\nPress Cancel to go back and edit the briefing, or OK to ignore the issue and continue.";
|
message += "\n\nPress Cancel to go back and edit the briefing, or OK to ignore the issue and continue.";
|
||||||
DialogResult dres = MessageBox.Show(message, "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
|
DialogResult dres = MessageBox.Show(message, "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
|
||||||
|
@ -16,6 +16,7 @@ namespace MobiusEditor.Interface
|
|||||||
{
|
{
|
||||||
public interface ICellOccupier
|
public interface ICellOccupier
|
||||||
{
|
{
|
||||||
|
/// <summary>Footprint of this object, determining where no other objects can be placed.</summary>
|
||||||
bool[,] OccupyMask { get; }
|
bool[,] OccupyMask { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,9 @@ namespace MobiusEditor.Interface
|
|||||||
{
|
{
|
||||||
public interface ICellOverlapper
|
public interface ICellOverlapper
|
||||||
{
|
{
|
||||||
|
/// <summary>Rectangular bounds of this overlapper.</summary>
|
||||||
Rectangle OverlapBounds { get; }
|
Rectangle OverlapBounds { get; }
|
||||||
|
/// <summary>Determines for each cell whether other graphics drawn under this one are considered to be mostly visible.</summary>
|
||||||
bool[,] OpaqueMask { get; }
|
bool[,] OpaqueMask { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,42 +38,113 @@ namespace MobiusEditor.Interface
|
|||||||
|
|
||||||
public interface IGamePlugin : IDisposable
|
public interface IGamePlugin : IDisposable
|
||||||
{
|
{
|
||||||
|
/// <summary>Name of the game.</summary>
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>The game type as enum.</summary>
|
||||||
GameType GameType { get; }
|
GameType GameType { get; }
|
||||||
|
|
||||||
|
/// <summary>True if the plugin is initialised to handle a megamap.</summary>
|
||||||
bool IsMegaMap { get; }
|
bool IsMegaMap { get; }
|
||||||
|
|
||||||
|
/// <summary>The map.</summary>
|
||||||
Map Map { get; }
|
Map Map { get; }
|
||||||
|
|
||||||
|
/// <summary>The map image to show.</summary>
|
||||||
Image MapImage { get; }
|
Image MapImage { get; }
|
||||||
|
|
||||||
|
/// <summary>Feedback handler that can be attached to the plugin.</summary>
|
||||||
IFeedBackHandler FeedBackHandler { get; set; }
|
IFeedBackHandler FeedBackHandler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>True if the currently loaded map was modified.</summary>
|
||||||
bool Dirty { get; set; }
|
bool Dirty { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Extra ini text that can be freely edited by the user.</summary>
|
||||||
string ExtraIniText { get; set; }
|
string ExtraIniText { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new map in the chosen theater.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="theater">The name of the theater to use.</param>
|
||||||
void New(string theater);
|
void New(string theater);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load a map.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path of the map to load.</param>
|
||||||
|
/// <param name="fileType">File type of the actual file in the path, so accompanying files can be loaded correctly.</param>
|
||||||
|
/// <returns>Any issues encountered when loading the map.</returns>
|
||||||
IEnumerable<string> Load(string path, FileType fileType);
|
IEnumerable<string> Load(string path, FileType fileType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the current map to the given path, with the given file type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path of the map to save.</param>
|
||||||
|
/// <param name="fileType">File type of the actual file in the path, so accompanying files can be saved correctly.</param>
|
||||||
|
/// <returns>true if the saving succeeded.</returns>
|
||||||
bool Save(string path, FileType fileType);
|
bool Save(string path, FileType fileType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the current map to the given path, with the given file type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Path of the map to save.</param>
|
||||||
|
/// <param name="fileType">File type of the actual file in the path, so accompanying files can be saved correctly.</param>
|
||||||
|
/// <param name="customPreview">Custom preview given to the map.</param>
|
||||||
|
/// <param name="dontResavePreview">True to not resave the preview on disc when doing the save operation.</param>
|
||||||
|
/// <returns>true if the saving succeeded.</returns>
|
||||||
bool Save(string path, FileType fileType, Bitmap customPreview, bool dontResavePreview);
|
bool Save(string path, FileType fileType, Bitmap customPreview, bool dontResavePreview);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate the map to see if there are any blocking errors preventing it from saving.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if the validation succeeded.</returns>
|
||||||
string Validate();
|
string Validate();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates an overview of how many items are on the map and how many are allowed, and does a trigger analysis.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The generated map items overview.</returns>
|
||||||
IEnumerable<string> AssessMapItems();
|
IEnumerable<string> AssessMapItems();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a hash set of all houses for which production is started by triggers.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A hash set of all houses for which production is started by triggers.</returns>
|
||||||
HashSet<string> GetHousesWithProduction();
|
HashSet<string> GetHousesWithProduction();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an array containing the reveal radius for each waypoint on the map.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="map">The map to gheck the waypoints on.</param>
|
||||||
|
/// <param name="forLargeReveal">False for small flare reveal, true for large area reveal.</param>
|
||||||
|
/// <returns></returns>
|
||||||
int[] GetRevealRadiusForWaypoints(Map map, bool forLargeReveal);
|
int[] GetRevealRadiusForWaypoints(Map map, bool forLargeReveal);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check whether there are any errors in the currect scripting.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="triggers">List of triggers to check.</param>
|
||||||
|
/// <param name="includeExternalData">True to fetch extra data from the map, such as map objects that triggers can be linked to.</param>
|
||||||
|
/// <param name="prefixNames">True to prefix the trigger name before every line. If false, each trigger's analysis will be preceded with a header line containing the trigger name.</param>
|
||||||
|
/// <param name="fatalOnly">True to report fatal issues only.</param>
|
||||||
|
/// <param name="fatal">Returns true if fatal issues were encountered.</param>
|
||||||
|
/// <param name="fix">True to fix issues that are encountered whenever possible.</param>
|
||||||
|
/// <param name="wasFixed">Returns true if issues were fixed.</param>
|
||||||
|
/// <returns>A summation of the encountered issues.</returns>
|
||||||
IEnumerable<string> CheckTriggers(IEnumerable<Trigger> triggers, bool includeExternalData, bool prefixNames, bool fatalOnly, out bool fatal, bool fix, out bool wasFixed);
|
IEnumerable<string> CheckTriggers(IEnumerable<Trigger> triggers, bool includeExternalData, bool prefixNames, bool fatalOnly, out bool fatal, bool fix, out bool wasFixed);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the name has a default map name that is considered empty by this game plugin.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">Map name to check.</param>
|
||||||
|
/// <returns>True if the given name is considered empty by this game plugin.</returns>
|
||||||
bool MapNameIsEmpty(string name);
|
bool MapNameIsEmpty(string name);
|
||||||
|
|
||||||
Boolean EvaluateBriefing(string briefing, out string message);
|
/// <summary>
|
||||||
|
/// Checks whether the briefing has any kind of issues concerning length or supported characters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="briefing">The briefing to check</param>
|
||||||
|
/// <returns>Null if everything is okay, otherwise any issues to show on the user interface.</returns>
|
||||||
|
string EvaluateBriefing(string briefing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,22 @@ namespace MobiusEditor.Interface
|
|||||||
{
|
{
|
||||||
public interface ITeamColor
|
public interface ITeamColor
|
||||||
{
|
{
|
||||||
|
/// <summary>Name of the color, used as identifier.</summary>
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
/// <summary>A general color representing this team color.</summary>
|
||||||
Color BaseColor { get; }
|
Color BaseColor { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply this color to a given image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The image to process.</param>
|
||||||
void ApplyToImage(Bitmap image);
|
void ApplyToImage(Bitmap image);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply this color to a given image, and output the actually-used bounds of the graphics.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">The image to process.</param>
|
||||||
|
/// <param name="opaqueBounds">The actually-used nontransparent area of the graphics that need to be painted.</param>
|
||||||
void ApplyToImage(Bitmap image, out Rectangle opaqueBounds);
|
void ApplyToImage(Bitmap image, out Rectangle opaqueBounds);
|
||||||
void ApplyToImage(byte[] bytes, int width, int height, int bytesPerPixel, int stride, Rectangle? opaqueBounds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,11 +16,17 @@ namespace MobiusEditor.Interface
|
|||||||
{
|
{
|
||||||
public interface ITechnoType: IBrowsableType
|
public interface ITechnoType: IBrowsableType
|
||||||
{
|
{
|
||||||
|
/// <summary>Object ID</summary>
|
||||||
sbyte ID { get; }
|
sbyte ID { get; }
|
||||||
|
/// <summary>Object ini name</summary>
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
/// <summary>True if this object has a weapon. This affects the default orders for placing it on the map.</summary>
|
||||||
bool IsArmed { get; }
|
bool IsArmed { get; }
|
||||||
|
/// <summary>True if this object is an aircraft, and is normally not placeable on the map.</summary>
|
||||||
bool IsAircraft { get; }
|
bool IsAircraft { get; }
|
||||||
|
/// <summary>True if this object is a fixed-wing aircraft. This treats it as 16-frame rotation, and affects the default orders for placing it on the map.</summary>
|
||||||
bool IsFixedWing { get; }
|
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; }
|
bool IsHarvester { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,23 @@ namespace MobiusEditor.Model
|
|||||||
[Flags]
|
[Flags]
|
||||||
public enum BuildingTypeFlag
|
public enum BuildingTypeFlag
|
||||||
{
|
{
|
||||||
|
/// <summary>No flags set.</summary>
|
||||||
None = 0,
|
None = 0,
|
||||||
|
/// <summary>Produces structures.</summary>
|
||||||
Factory = (1 << 0),
|
Factory = (1 << 0),
|
||||||
|
/// <summary>Has a bib attached.</summary>
|
||||||
Bib = (1 << 1),
|
Bib = (1 << 1),
|
||||||
|
/// <summary>Is a fake building.</summary>
|
||||||
Fake = (1 << 2),
|
Fake = (1 << 2),
|
||||||
|
/// <summary>Has a rotating turret, and accepts a Facing value in the ini file.</summary>
|
||||||
Turret = (1 << 3),
|
Turret = (1 << 3),
|
||||||
|
/// <summary>Only has a single frame of graphics.</summary>
|
||||||
SingleFrame = (1 << 4),
|
SingleFrame = (1 << 4),
|
||||||
|
/// <summary>Does not adjust to house colors.</summary>
|
||||||
NoRemap = (1 << 5),
|
NoRemap = (1 << 5),
|
||||||
|
/// <summary>Is flat on the ground; anything around will overlap this building's graphics.</summary>
|
||||||
Flat = (1 << 6),
|
Flat = (1 << 6),
|
||||||
|
/// <summary>Can show a gap area-of-effect radius indicator.</summary>
|
||||||
IsGapGenerator = (1 << 7),
|
IsGapGenerator = (1 << 7),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +79,8 @@ namespace MobiusEditor.Model
|
|||||||
get { return (Flag & BuildingTypeFlag.Bib) == BuildingTypeFlag.Bib; }
|
get { return (Flag & BuildingTypeFlag.Bib) == BuildingTypeFlag.Bib; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value)
|
// Bibs are only supported for widths 2 to 4
|
||||||
|
if (value && Size.Width >= 2 && Size.Width <= 4)
|
||||||
{
|
{
|
||||||
Flag |= BuildingTypeFlag.Bib;
|
Flag |= BuildingTypeFlag.Bib;
|
||||||
}
|
}
|
||||||
@ -118,7 +128,8 @@ namespace MobiusEditor.Model
|
|||||||
this.OwnerHouse = ownerHouse;
|
this.OwnerHouse = ownerHouse;
|
||||||
this.Theaters = theaters;
|
this.Theaters = theaters;
|
||||||
this.FactoryOverlay = factoryOverlay;
|
this.FactoryOverlay = factoryOverlay;
|
||||||
this.RecalculateBibs();
|
// Check on width and disable if needed. This also calls RecalculateBibs.
|
||||||
|
this.HasBib = this.HasBib;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuildingType(sbyte id, string name, string textId, int powerProd, int powerUse, int width, int height, string occupyMask, string ownerHouse, TheaterType[] theaters, String graphicsSource, BuildingTypeFlag flag)
|
public BuildingType(sbyte id, string name, string textId, int powerProd, int powerUse, int width, int height, string occupyMask, string ownerHouse, TheaterType[] theaters, String graphicsSource, BuildingTypeFlag flag)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -24,16 +24,27 @@ namespace MobiusEditor.Model
|
|||||||
[Flags]
|
[Flags]
|
||||||
public enum OverlayTypeFlag
|
public enum OverlayTypeFlag
|
||||||
{
|
{
|
||||||
|
/// <summary>No flags set.</summary>
|
||||||
None = 0,
|
None = 0,
|
||||||
|
/// <summary>Is a basic resource overlay.</summary>
|
||||||
TiberiumOrGold = (1 << 0),
|
TiberiumOrGold = (1 << 0),
|
||||||
|
/// <summary>Is a high value resource overlay.</summary>
|
||||||
Gems = (1 << 1),
|
Gems = (1 << 1),
|
||||||
|
/// <summary>Is a wall.</summary>
|
||||||
Wall = (1 << 2),
|
Wall = (1 << 2),
|
||||||
|
/// <summary>Is a wooden crate. This affects the color of the outline it gets.</summary>
|
||||||
WoodCrate = (1 << 3),
|
WoodCrate = (1 << 3),
|
||||||
|
/// <summary>Is a steel crate. This affects the color of the outline it gets.</summary>
|
||||||
SteelCrate = (1 << 4),
|
SteelCrate = (1 << 4),
|
||||||
|
/// <summary>Is the flag placement indicator.</summary>
|
||||||
Flag = (1 << 5),
|
Flag = (1 << 5),
|
||||||
|
/// <summary>Is a pavement type.</summary>
|
||||||
Pavement = (1 << 6),
|
Pavement = (1 << 6),
|
||||||
|
/// <summary>Needs to use the special concrete pavement connection logic.</summary>
|
||||||
Concrete = (1 << 7),
|
Concrete = (1 << 7),
|
||||||
|
/// <summary>Is a solid object that obstructs placement.</summary>
|
||||||
Solid = (1 << 8),
|
Solid = (1 << 8),
|
||||||
|
/// <summary>Is a crate.</summary>
|
||||||
Crate = WoodCrate | SteelCrate,
|
Crate = WoodCrate | SteelCrate,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,12 +25,27 @@ namespace MobiusEditor.Model
|
|||||||
[Flags]
|
[Flags]
|
||||||
public enum TemplateTypeFlag
|
public enum TemplateTypeFlag
|
||||||
{
|
{
|
||||||
|
/// <summary>No flags set.</summary>
|
||||||
None = 0,
|
None = 0,
|
||||||
|
/// <summary>Used to filter out default terrain.</summary>
|
||||||
Clear = (1 << 0),
|
Clear = (1 << 0),
|
||||||
Water = (1 << 1),
|
Water = (1 << 1),
|
||||||
|
/// <summary>This tileset is 1x1, and any additional tiles it contains are treated as randomisable alternate tiles, not as parts of a larger shape.</summary>
|
||||||
RandomCell = (1 << 2),
|
RandomCell = (1 << 2),
|
||||||
|
/// <summary>
|
||||||
|
/// This is a virtual tileset group, used to group together loose equivalent 1x1 tiles as randomisable. It should never actually be placed down on the map.
|
||||||
|
/// GroupTiles will contain all tiles that are part of this group.
|
||||||
|
/// </summary>
|
||||||
Group = RandomCell | (1 << 3),
|
Group = RandomCell | (1 << 3),
|
||||||
|
/// <summary>
|
||||||
|
/// This tile is a 1x1 tile that has a bunch of equivalent alternates, and because of that, it is grouped in a virtual tileset group as randomisable.
|
||||||
|
/// GroupTiles will contain the group it belongs to, so picking it from the map can easily select the group it belongs to.
|
||||||
|
/// </summary>
|
||||||
IsGrouped = (1 << 4),
|
IsGrouped = (1 << 4),
|
||||||
|
/// <summary>
|
||||||
|
/// This tileset has equivalent tilesets, and when drag-placing this, it will switch to randomly placing the alternates as well.
|
||||||
|
/// GroupTiles contains all equivalents that can be used.
|
||||||
|
/// </summary>
|
||||||
HasEquivalents = (1 << 5),
|
HasEquivalents = (1 << 5),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +215,8 @@ namespace MobiusEditor.Model
|
|||||||
/// <param name="iconHeight">Height in cells.</param>
|
/// <param name="iconHeight">Height in cells.</param>
|
||||||
/// <param name="theaters">Theaters that contain this tile.</param>
|
/// <param name="theaters">Theaters that contain this tile.</param>
|
||||||
/// <param name="maskOverrides">Mask override for tiles that contain too many graphics in the Remaster. Indices with '0' are removed from the tiles. Spaces are ignored and can be added for visual separation.</param>
|
/// <param name="maskOverrides">Mask override for tiles that contain too many graphics in the Remaster. Indices with '0' are removed from the tiles. Spaces are ignored and can be added for visual separation.</param>
|
||||||
/// <param name="equivalentOffset"></param>
|
/// <param name="equivalentOffset">Thez position this tileset should be placed on when used as equivalent.</param>
|
||||||
/// <param name="equivalentTiles"></param>
|
/// <param name="equivalentTiles">Equivalent tiles that can be placed down when drag-placing multiple of this.</param>
|
||||||
public TemplateType(ushort id, string name, int iconWidth, int iconHeight, TheaterType[] theaters, string[] maskOverrides, Point equivalentOffset, string[] equivalentTiles)
|
public TemplateType(ushort id, string name, int iconWidth, int iconHeight, TheaterType[] theaters, string[] maskOverrides, Point equivalentOffset, string[] equivalentTiles)
|
||||||
: this(id, name, iconWidth, iconHeight, theaters, TemplateTypeFlag.None, maskOverrides)
|
: this(id, name, iconWidth, iconHeight, theaters, TemplateTypeFlag.None, maskOverrides)
|
||||||
{
|
{
|
||||||
@ -230,7 +245,6 @@ namespace MobiusEditor.Model
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a TemplateType object.
|
/// Creates a TemplateType object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -244,6 +258,7 @@ namespace MobiusEditor.Model
|
|||||||
: this(id, name, iconWidth, iconHeight, theaters, TemplateTypeFlag.None, maskOverride)
|
: this(id, name, iconWidth, iconHeight, theaters, TemplateTypeFlag.None, maskOverride)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a TemplateType object.
|
/// Creates a TemplateType object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -293,7 +308,6 @@ namespace MobiusEditor.Model
|
|||||||
{
|
{
|
||||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.Equals(obj);
|
return base.Equals(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,14 +23,23 @@ namespace MobiusEditor.Model
|
|||||||
[Flags]
|
[Flags]
|
||||||
public enum UnitTypeFlag
|
public enum UnitTypeFlag
|
||||||
{
|
{
|
||||||
|
/// <summary>No flags set.</summary>
|
||||||
None = 0,
|
None = 0,
|
||||||
|
/// <summary>Is a fixed-wing airplane. This treats it as 16-frame rotation, and affects the default orders for placing it on the map.</summary>
|
||||||
IsFixedWing = 1 << 0,
|
IsFixedWing = 1 << 0,
|
||||||
|
/// <summary>Has a turret drawn on the unit.</summary>
|
||||||
HasTurret = 1 << 1,
|
HasTurret = 1 << 1,
|
||||||
|
/// <summary>Needs to render two turrets.</summary>
|
||||||
HasDoubleTurret = 1 << 2,
|
HasDoubleTurret = 1 << 2,
|
||||||
|
/// <summary>Can attack units. This affects the default orders for placing it on the map.</summary>
|
||||||
IsArmed = 1 << 3,
|
IsArmed = 1 << 3,
|
||||||
|
/// <summary>Can harvest resources. This affects the default orders for placing it on the map.</summary>
|
||||||
IsHarvester = 1 << 4,
|
IsHarvester = 1 << 4,
|
||||||
|
/// <summary>Is a unit that is filtered out of the lists if expansion units are disabled.</summary>
|
||||||
IsExpansionUnit = 1 << 5,
|
IsExpansionUnit = 1 << 5,
|
||||||
|
/// <summary>Can show a mobile gap area-of-effect radius indicator.</summary>
|
||||||
IsGapGenerator = 1 << 6,
|
IsGapGenerator = 1 << 6,
|
||||||
|
/// <summary>Can show a radar jamming area-of-effect radius indicator.</summary>
|
||||||
IsJammer = 1 << 7,
|
IsJammer = 1 << 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2314,7 +2314,7 @@ namespace MobiusEditor.RedAlert
|
|||||||
{
|
{
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
List<(Point p, Building b)> foundBuildings = buildings.Where(lo => bType.Name.Equals(lo.Occupier.Type.Name, StringComparison.InvariantCultureIgnoreCase))
|
List<(Point p, Building b)> foundBuildings = buildings.Where(lo => bType.ID == lo.Occupier.Type.ID)
|
||||||
.OrderBy(lo => lo.Location.Y * map.Metrics.Width + lo.Location.X).ToList();
|
.OrderBy(lo => lo.Location.Y * map.Metrics.Width + lo.Location.X).ToList();
|
||||||
bType.HasBib = hasBib;
|
bType.HasBib = hasBib;
|
||||||
foreach ((Point p, Building b) in foundBuildings)
|
foreach ((Point p, Building b) in foundBuildings)
|
||||||
@ -2535,7 +2535,7 @@ namespace MobiusEditor.RedAlert
|
|||||||
}
|
}
|
||||||
string nameToIndexString<T>(IList<T> list, string name) => nameToIndex(list, name).ToString();
|
string nameToIndexString<T>(IList<T> list, string name) => nameToIndex(list, name).ToString();
|
||||||
INISection teamTypesSection = ini.Sections.Add("TeamTypes");
|
INISection teamTypesSection = ini.Sections.Add("TeamTypes");
|
||||||
foreach (TeamType teamType in Map.TeamTypes.OrderBy(t => t.Name.ToUpperInvariant()))
|
foreach (TeamType teamType in Map.TeamTypes)
|
||||||
{
|
{
|
||||||
string[] classes = teamType.Classes
|
string[] classes = teamType.Classes
|
||||||
.Select(c => string.Format("{0}:{1}", c.Type.Name.ToUpperInvariant(), c.Count))
|
.Select(c => string.Format("{0}:{1}", c.Type.Name.ToUpperInvariant(), c.Count))
|
||||||
@ -3586,19 +3586,19 @@ namespace MobiusEditor.RedAlert
|
|||||||
return String.IsNullOrEmpty(name) || "<none>".Equals(name, StringComparison.OrdinalIgnoreCase);
|
return String.IsNullOrEmpty(name) || "<none>".Equals(name, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EvaluateBriefing(string briefing, out string message)
|
public string EvaluateBriefing(string briefing)
|
||||||
{
|
{
|
||||||
bool briefLenOvfl = false;
|
bool briefLenOvfl = false;
|
||||||
bool briefLenSplitOvfl = false;
|
bool briefLenSplitOvfl = false;
|
||||||
const int cutoff = 40;
|
const int cutoff = 40;
|
||||||
string brief = briefing.Replace('\t', ' ').Trim('\r', '\n', ' ').Replace("\r\n", "\n").Replace("\r", "\n");
|
string briefText = (briefing ?? String.Empty).Replace('\t', ' ').Trim('\r', '\n', ' ').Replace("\r\n", "\n").Replace("\r", "\n");
|
||||||
int lines = brief.Count(c => c == '\n') + 1;
|
int lines = briefText.Count(c => c == '\n') + 1;
|
||||||
briefLenOvfl = lines > 25;
|
briefLenOvfl = lines > 25;
|
||||||
if (!briefLenOvfl)
|
if (!briefLenOvfl)
|
||||||
{
|
{
|
||||||
// split in lines of 40; that's more or less the average line length in the brief screen.
|
// split in lines of 40; that's more or less the average line length in the brief screen.
|
||||||
List<string> txtLines = new List<string>();
|
List<string> txtLines = new List<string>();
|
||||||
string[] briefLines = brief.Split('\n');
|
string[] briefLines = briefText.Split('\n');
|
||||||
for (int i = 0; i < briefLines.Length; ++i)
|
for (int i = 0; i < briefLines.Length; ++i)
|
||||||
{
|
{
|
||||||
string line = briefLines[i].Trim();
|
string line = briefLines[i].Trim();
|
||||||
@ -3627,7 +3627,7 @@ namespace MobiusEditor.RedAlert
|
|||||||
briefLenSplitOvfl = txtLines.Count > 25;
|
briefLenSplitOvfl = txtLines.Count > 25;
|
||||||
}
|
}
|
||||||
const string warn25Lines = "Red Alert's briefing screen in the Remaster can only show 25 lines of briefing text. ";
|
const string warn25Lines = "Red Alert's briefing screen in the Remaster can only show 25 lines of briefing text. ";
|
||||||
message = null;
|
string message = null;
|
||||||
if (briefLenOvfl)
|
if (briefLenOvfl)
|
||||||
{
|
{
|
||||||
message = warn25Lines + "Your current briefing exceeds that.";
|
message = warn25Lines + "Your current briefing exceeds that.";
|
||||||
@ -3636,7 +3636,7 @@ namespace MobiusEditor.RedAlert
|
|||||||
{
|
{
|
||||||
message = warn25Lines + "The lines average to about 40 characters per line, and when split that way, your current briefing exceeds that, meaning it will most likely not display correctly in-game.";
|
message = warn25Lines + "The lines average to about 40 characters per line, and when split that way, your current briefing exceeds that, meaning it will most likely not display correctly in-game.";
|
||||||
}
|
}
|
||||||
if (Globals.WriteClassicBriefing && brief.Length > maxBriefLengthClassic)
|
if (Globals.WriteClassicBriefing && briefText.Length > maxBriefLengthClassic)
|
||||||
{
|
{
|
||||||
if (message == null)
|
if (message == null)
|
||||||
{
|
{
|
||||||
@ -3648,7 +3648,7 @@ namespace MobiusEditor.RedAlert
|
|||||||
}
|
}
|
||||||
message += "Classic Red Alert briefings cannot exceed " + maxBriefLengthClassic + " characters. This includes line breaks.\n\nThis will not affect the mission when playing in the Remaster, but the briefing will be truncated when playing in the original game.";
|
message += "Classic Red Alert briefings cannot exceed " + maxBriefLengthClassic + " characters. This includes line breaks.\n\nThis will not affect the mission when playing in the Remaster, but the briefing will be truncated when playing in the original game.";
|
||||||
}
|
}
|
||||||
return message != null;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BasicSection_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
private void BasicSection_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
@ -752,21 +752,22 @@ namespace MobiusEditor.Render
|
|||||||
string turret2Name = unit.Type.HasDoubleTurret ? unit.Type.SecondTurret ?? unit.Type.Turret ?? unit.Type.Name : null;
|
string turret2Name = unit.Type.HasDoubleTurret ? unit.Type.SecondTurret ?? unit.Type.Turret ?? unit.Type.Name : null;
|
||||||
int turretIcon = unit.Type.Name.Equals(turretName, StringComparison.OrdinalIgnoreCase) ? icon + 32 : icon;
|
int turretIcon = unit.Type.Name.Equals(turretName, StringComparison.OrdinalIgnoreCase) ? icon + 32 : icon;
|
||||||
int turret2Icon = unit.Type.Name.Equals(turret2Name, StringComparison.OrdinalIgnoreCase) ? icon + 32 : icon;
|
int turret2Icon = unit.Type.Name.Equals(turret2Name, StringComparison.OrdinalIgnoreCase) ? icon + 32 : icon;
|
||||||
|
// Special frame handling
|
||||||
if (gameType == GameType.RedAlert)
|
if (gameType == GameType.RedAlert)
|
||||||
{
|
{
|
||||||
if (unit.Type == RedAlert.UnitTypes.Phase)
|
if (unit.Type.ID == RedAlert.UnitTypes.Phase.ID)
|
||||||
{
|
{
|
||||||
// Compensate for unload frames.
|
// Compensate for unload frames.
|
||||||
turretIcon += 6;
|
turretIcon += 6;
|
||||||
}
|
}
|
||||||
else if (unit.Type == RedAlert.UnitTypes.MGG)
|
else if (unit.Type.ID == RedAlert.UnitTypes.MGG.ID)
|
||||||
{
|
{
|
||||||
// 16-frame rotation, but saved as 8 frames because the other 8 are identical.
|
// 16-frame rotation, but saved as 8 frames because the other 8 are identical.
|
||||||
turretIcon = 32 + ((icon / 2) & 7);
|
turretIcon = 32 + ((icon / 2) & 7);
|
||||||
}
|
}
|
||||||
else if (unit.Type == RedAlert.UnitTypes.Tesla)
|
else if (unit.Type.ID == RedAlert.UnitTypes.Tesla.ID)
|
||||||
{
|
{
|
||||||
// Fixed turret frame.
|
// turret is an animation rather than a rotation; always take the first frame.
|
||||||
turretIcon = 32;
|
turretIcon = 32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -874,26 +875,26 @@ namespace MobiusEditor.Render
|
|||||||
turret2Adjust.Y += unit.Type.TurretY;
|
turret2Adjust.Y += unit.Type.TurretY;
|
||||||
}
|
}
|
||||||
Point center = new Point(renderBounds.Width / 2, renderBounds.Height / 2);
|
Point center = new Point(renderBounds.Width / 2, renderBounds.Height / 2);
|
||||||
if (turretTile != null) {
|
|
||||||
Size turretSize = turretTile.Image.Size;
|
void RenderTurret(Graphics ug, Tile turrTile, Point turrAdjust, Size tSize)
|
||||||
var turretRenderSize = new Size(turretSize.Width * tileSize.Width / Globals.OriginalTileWidth, turretSize.Height * tileSize.Height / Globals.OriginalTileHeight);
|
{
|
||||||
var turretBounds = new Rectangle(center - new Size(turretRenderSize.Width / 2, turretRenderSize.Height / 2), turretRenderSize);
|
Size turretSize = turrTile.Image.Size;
|
||||||
turretBounds.Offset(
|
var turretRenderSize = new Size(turretSize.Width * tSize.Width / Globals.OriginalTileWidth, turretSize.Height * tSize.Height / Globals.OriginalTileHeight);
|
||||||
turretAdjust.X * tileSize.Width / Globals.PixelWidth,
|
var turrBounds = new Rectangle(center - new Size(turretRenderSize.Width / 2, turretRenderSize.Height / 2), turretRenderSize);
|
||||||
turretAdjust.Y * tileSize.Height / Globals.PixelHeight
|
turrBounds.Offset(
|
||||||
|
turrAdjust.X * tSize.Width / Globals.PixelWidth,
|
||||||
|
turrAdjust.Y * tSize.Height / Globals.PixelHeight
|
||||||
);
|
);
|
||||||
unitG.DrawImage(turretTile.Image, turretBounds, 0, 0, turretTile.Image.Width, turretTile.Image.Height, GraphicsUnit.Pixel);
|
ug.DrawImage(turrTile.Image, turrBounds, 0, 0, turrTile.Image.Width, turrTile.Image.Height, GraphicsUnit.Pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turretTile != null)
|
||||||
|
{
|
||||||
|
RenderTurret(unitG, turretTile, turretAdjust, tileSize);
|
||||||
}
|
}
|
||||||
if (unit.Type.HasDoubleTurret && turret2Tile != null)
|
if (unit.Type.HasDoubleTurret && turret2Tile != null)
|
||||||
{
|
{
|
||||||
Size turret2Size = turret2Tile.Image.Size;
|
RenderTurret(unitG, turret2Tile, turret2Adjust, tileSize);
|
||||||
var turret2RenderSize = new Size(turret2Size.Width * tileSize.Width / Globals.OriginalTileWidth, turret2Size.Height * tileSize.Height / Globals.OriginalTileHeight);
|
|
||||||
var turret2Bounds = new Rectangle(center - new Size(turret2RenderSize.Width / 2, turret2RenderSize.Height / 2), turret2RenderSize);
|
|
||||||
turret2Bounds.Offset(
|
|
||||||
turret2Adjust.X * tileSize.Width / Globals.PixelWidth,
|
|
||||||
turret2Adjust.Y * tileSize.Height / Globals.PixelHeight
|
|
||||||
);
|
|
||||||
unitG.DrawImage(turret2Tile.Image, turret2Bounds, 0, 0, turret2Tile.Image.Width, turret2Tile.Image.Height, GraphicsUnit.Pixel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1571,7 +1572,7 @@ namespace MobiusEditor.Render
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ITeamColor tc = building.Type.CanRemap ? Globals.TheTeamColorManager[building.House.BuildingTeamColor] : null;
|
ITeamColor tc = Globals.TheTeamColorManager[building.House.BuildingTeamColor];
|
||||||
Color circleColor = tc?.BaseColor ?? Globals.TheTeamColorManager.RemapBaseColor;
|
Color circleColor = tc?.BaseColor ?? Globals.TheTeamColorManager.RemapBaseColor;
|
||||||
bool[,] cells = building.Type.BaseOccupyMask;
|
bool[,] cells = building.Type.BaseOccupyMask;
|
||||||
int maskY = cells.GetLength(0);
|
int maskY = cells.GetLength(0);
|
||||||
@ -1599,12 +1600,7 @@ namespace MobiusEditor.Render
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String teamColor;
|
ITeamColor tc = Globals.TheTeamColorManager[unit.House.BuildingTeamColor];
|
||||||
if (!unit.House.OverrideTeamColors.TryGetValue(unit.Type.Name, out teamColor))
|
|
||||||
{
|
|
||||||
teamColor = unit.House.UnitTeamColor;
|
|
||||||
}
|
|
||||||
ITeamColor tc = Globals.TheTeamColorManager[teamColor];
|
|
||||||
Color circleColor = tc?.BaseColor ?? Globals.TheTeamColorManager.RemapBaseColor;
|
Color circleColor = tc?.BaseColor ?? Globals.TheTeamColorManager.RemapBaseColor;
|
||||||
Color alphacorr = Color.FromArgb(unit.Tint.A * 128 / 256, circleColor);
|
Color alphacorr = Color.FromArgb(unit.Tint.A * 128 / 256, circleColor);
|
||||||
if (isJammer)
|
if (isJammer)
|
||||||
|
@ -275,10 +275,9 @@ namespace MobiusEditor.SoleSurvivor
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Boolean EvaluateBriefing(string briefing, out string message)
|
public override string EvaluateBriefing(string briefing)
|
||||||
{
|
{
|
||||||
message = null;
|
return null;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3201,21 +3201,21 @@ namespace MobiusEditor.TiberianDawn
|
|||||||
return String.IsNullOrEmpty(name) || "None".Equals(name, StringComparison.OrdinalIgnoreCase);
|
return String.IsNullOrEmpty(name) || "None".Equals(name, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual bool EvaluateBriefing(string briefing, out string message)
|
public virtual string EvaluateBriefing(string briefing)
|
||||||
{
|
{
|
||||||
message = null;
|
string message = null;
|
||||||
if (!Globals.WriteClassicBriefing)
|
if (!Globals.WriteClassicBriefing)
|
||||||
{
|
{
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
string briefText = briefing.Replace('\t', ' ').Trim('\r', '\n', ' ').Replace("\r\n", "\r").Replace("\n", "\r");
|
string briefText = (briefing ?? String.Empty).Replace('\t', ' ').Trim('\r', '\n', ' ').Replace("\r\n", "\n").Replace("\r", "\n");
|
||||||
// Remove duplicate spaces
|
// Remove duplicate spaces
|
||||||
briefText = Regex.Replace(briefText, " +", " ");
|
briefText = Regex.Replace(briefText, " +", " ");
|
||||||
if (briefText.Length > maxBriefLengthClassic)
|
if (briefText.Length > maxBriefLengthClassic)
|
||||||
{
|
{
|
||||||
message = "Classic Tiberian Dawn briefings cannot exceed " + maxBriefLengthClassic + " characters. This includes line breaks.\n\nThis will not affect the mission when playing in the Remaster, but the briefing will be truncated when playing in the original game.";
|
message = "Classic Tiberian Dawn briefings cannot exceed " + maxBriefLengthClassic + " characters. This includes line breaks.\n\nThis will not affect the mission when playing in the Remaster, but the briefing will be truncated when playing in the original game.";
|
||||||
}
|
}
|
||||||
return message == null;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void BasicSection_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
protected void BasicSection_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
@ -23,7 +23,6 @@ namespace MobiusEditor.Tools.Dialogs
|
|||||||
{
|
{
|
||||||
public partial class ResourcesToolDialog : ToolDialog<ResourcesTool>
|
public partial class ResourcesToolDialog : ToolDialog<ResourcesTool>
|
||||||
{
|
{
|
||||||
//public Label TotalResourcesLbl => lblResTotalVal;
|
|
||||||
public Label BoundsResourcesLbl => lblResBoundsVal;
|
public Label BoundsResourcesLbl => lblResBoundsVal;
|
||||||
|
|
||||||
public NumericUpDown ResourceBrushSizeNud => nudBrushSize;
|
public NumericUpDown ResourceBrushSizeNud => nudBrushSize;
|
||||||
@ -38,7 +37,7 @@ namespace MobiusEditor.Tools.Dialogs
|
|||||||
protected override void InitializeInternal(MapPanel mapPanel, MapLayerFlag activeLayers, ToolStripStatusLabel toolStatusLabel,
|
protected override void InitializeInternal(MapPanel mapPanel, MapLayerFlag activeLayers, ToolStripStatusLabel toolStatusLabel,
|
||||||
ToolTip mouseToolTip, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> undoRedoList)
|
ToolTip mouseToolTip, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> undoRedoList)
|
||||||
{
|
{
|
||||||
Tool = new ResourcesTool(mapPanel, activeLayers, toolStatusLabel, null, BoundsResourcesLbl,
|
Tool = new ResourcesTool(mapPanel, activeLayers, toolStatusLabel, BoundsResourcesLbl,
|
||||||
ResourceBrushSizeNud, GemsCheckBox, plugin, undoRedoList);
|
ResourceBrushSizeNud, GemsCheckBox, plugin, undoRedoList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ namespace MobiusEditor.Tools
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected override MapLayerFlag ManuallyHandledLayers => MapLayerFlag.None;
|
protected override MapLayerFlag ManuallyHandledLayers => MapLayerFlag.None;
|
||||||
|
|
||||||
private readonly Label totalResourcesLbl;
|
|
||||||
private readonly Label boundsResourcesLbl;
|
private readonly Label boundsResourcesLbl;
|
||||||
private readonly NumericUpDown brushSizeNud;
|
private readonly NumericUpDown brushSizeNud;
|
||||||
private readonly CheckBox gemsCheckBox;
|
private readonly CheckBox gemsCheckBox;
|
||||||
@ -51,11 +50,10 @@ namespace MobiusEditor.Tools
|
|||||||
private readonly Dictionary<int, Overlay> undoOverlays = new Dictionary<int, Overlay>();
|
private readonly Dictionary<int, Overlay> undoOverlays = new Dictionary<int, Overlay>();
|
||||||
private readonly Dictionary<int, Overlay> redoOverlays = new Dictionary<int, Overlay>();
|
private readonly Dictionary<int, Overlay> redoOverlays = new Dictionary<int, Overlay>();
|
||||||
|
|
||||||
public ResourcesTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, Label totalResourcesLbl, Label boundsResourcesLbl,
|
public ResourcesTool(MapPanel mapPanel, MapLayerFlag layers, ToolStripStatusLabel statusLbl, Label boundsResourcesLbl,
|
||||||
NumericUpDown brushSizeNud, CheckBox gemsCheckBox, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
|
NumericUpDown brushSizeNud, CheckBox gemsCheckBox, IGamePlugin plugin, UndoRedoList<UndoRedoEventArgs> url)
|
||||||
: base(mapPanel, layers, statusLbl, plugin, url)
|
: base(mapPanel, layers, statusLbl, plugin, url)
|
||||||
{
|
{
|
||||||
this.totalResourcesLbl = totalResourcesLbl;
|
|
||||||
this.boundsResourcesLbl = boundsResourcesLbl;
|
this.boundsResourcesLbl = boundsResourcesLbl;
|
||||||
this.brushSizeNud = brushSizeNud;
|
this.brushSizeNud = brushSizeNud;
|
||||||
this.gemsCheckBox = gemsCheckBox;
|
this.gemsCheckBox = gemsCheckBox;
|
||||||
@ -315,11 +313,7 @@ namespace MobiusEditor.Tools
|
|||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (totalResourcesLbl != null)
|
boundsResourcesLbl.Text = map.ResourcesOnMap.ToString();
|
||||||
{
|
|
||||||
totalResourcesLbl.Text = map.TotalResources.ToString();
|
|
||||||
}
|
|
||||||
boundsResourcesLbl.Text = map.ResourcesInBounds.ToString();
|
|
||||||
if (map.OverlayTypes.Any(t => t.IsGem))
|
if (map.OverlayTypes.Any(t => t.IsGem))
|
||||||
{
|
{
|
||||||
gemsCheckBox.Visible = true;
|
gemsCheckBox.Visible = true;
|
||||||
|
@ -379,11 +379,36 @@ namespace MobiusEditor.Utility
|
|||||||
BitmapData data = null;
|
BitmapData data = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
|
PixelFormat dataFormat = bitmap.PixelFormat;
|
||||||
var bpp = Image.GetPixelFormatSize(data.PixelFormat) / 8;
|
if (dataFormat == PixelFormat.Format24bppRgb || dataFormat == PixelFormat.Format32bppRgb)
|
||||||
|
{
|
||||||
|
return new Rectangle(0, 0, bitmap.Width, bitmap.Height);
|
||||||
|
}
|
||||||
|
List<int> transColors = null;
|
||||||
|
if ((dataFormat & PixelFormat.Indexed) == PixelFormat.Indexed)
|
||||||
|
{
|
||||||
|
dataFormat = PixelFormat.Format8bppIndexed;
|
||||||
|
Color[] entries = bitmap.Palette.Entries;
|
||||||
|
transColors = new List<int>();
|
||||||
|
for (Int32 i = 0; i < entries.Length; i++)
|
||||||
|
{
|
||||||
|
if (entries[i].A == 0)
|
||||||
|
transColors.Add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, dataFormat);
|
||||||
var bytes = new byte[data.Stride * data.Height];
|
var bytes = new byte[data.Stride * data.Height];
|
||||||
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
|
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
|
||||||
return ImageUtils.CalculateOpaqueBounds(bytes, data.Width, data.Height, bpp, data.Stride);
|
if (bitmap.PixelFormat == PixelFormat.Format8bppIndexed)
|
||||||
|
{
|
||||||
|
return CalculateOpaqueBounds8bpp(bytes, data.Width, data.Height, data.Stride, transColors);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var bpp = Image.GetPixelFormatSize(dataFormat) / 8;
|
||||||
|
return CalculateOpaqueBoundsHiCol(bytes, data.Width, data.Height, bpp, data.Stride);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -394,8 +419,22 @@ namespace MobiusEditor.Utility
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Rectangle CalculateOpaqueBounds(byte[] data, int width, int height, int bytespp, int stride)
|
/// <summary>
|
||||||
|
/// Calculates the actually opaque bounds of a 24bpp or 32bpp image given as bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Image data.</param>
|
||||||
|
/// <param name="width">Image width.</param>
|
||||||
|
/// <param name="height">Image height.</param>
|
||||||
|
/// <param name="bytespp">Bytes per pixel.</param>
|
||||||
|
/// <param name="stride">Stride of the image.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Rectangle CalculateOpaqueBoundsHiCol(byte[] data, int width, int height, int bytespp, int stride)
|
||||||
{
|
{
|
||||||
|
// Only handle 32bpp data.
|
||||||
|
if (bytespp != 4)
|
||||||
|
{
|
||||||
|
return new Rectangle(0, 0, width, height);
|
||||||
|
}
|
||||||
// Modified this function to result in (0,0,0,0) when the image is empty, rather than retaining the full size.
|
// Modified this function to result in (0,0,0,0) when the image is empty, rather than retaining the full size.
|
||||||
int lineWidth = width * bytespp;
|
int lineWidth = width * bytespp;
|
||||||
bool isTransparentRow(int y)
|
bool isTransparentRow(int y)
|
||||||
@ -462,5 +501,77 @@ namespace MobiusEditor.Utility
|
|||||||
return opaqueBounds;
|
return opaqueBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Rectangle CalculateOpaqueBounds8bpp(byte[] data, int width, int height, int stride, List<int> transparentColors)
|
||||||
|
{
|
||||||
|
HashSet<int> trMap = new HashSet<int>(transparentColors);
|
||||||
|
// Only handle 32bpp data.
|
||||||
|
// Modified this function to result in (0,0,0,0) when the image is empty, rather than retaining the full size.
|
||||||
|
int lineWidth = width;
|
||||||
|
bool isTransparentRow(int y)
|
||||||
|
{
|
||||||
|
int start = y * stride;
|
||||||
|
int end = start + lineWidth;
|
||||||
|
for (var i = start; i < end; ++i)
|
||||||
|
{
|
||||||
|
if (!trMap.Contains(data[i]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var opaqueBounds = new Rectangle(0, 0, width, height);
|
||||||
|
for (int y = height - 1; y >= 0; --y)
|
||||||
|
{
|
||||||
|
if (!isTransparentRow(y))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
opaqueBounds.Height = y;
|
||||||
|
}
|
||||||
|
int endHeight = opaqueBounds.Height;
|
||||||
|
for (int y = 0; y < endHeight; ++y)
|
||||||
|
{
|
||||||
|
if (!isTransparentRow(y))
|
||||||
|
{
|
||||||
|
opaqueBounds.Y = y;
|
||||||
|
opaqueBounds.Height = endHeight - y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool isTransparentColumn(int x)
|
||||||
|
{
|
||||||
|
var pos = opaqueBounds.Top * stride + x;
|
||||||
|
for (var y = 0; y < opaqueBounds.Height; ++y)
|
||||||
|
{
|
||||||
|
if (!trMap.Contains(data[pos]))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pos += stride;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (int x = width - 1; x >= 0; --x)
|
||||||
|
{
|
||||||
|
if (!isTransparentColumn(x))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
opaqueBounds.Width = x;
|
||||||
|
}
|
||||||
|
int endWidth = opaqueBounds.Width;
|
||||||
|
for (int x = 0; x < endWidth; ++x)
|
||||||
|
{
|
||||||
|
if (!isTransparentColumn(x))
|
||||||
|
{
|
||||||
|
opaqueBounds.X = x;
|
||||||
|
opaqueBounds.Width = endWidth - x;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opaqueBounds;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,6 @@ namespace MobiusEditor.Utility
|
|||||||
|
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
private byte[] classicRemap;
|
|
||||||
public byte[] ClassicRemap => classicRemap ?? (Variant == null ? null : teamColorManager.GetItem(Variant)?.ClassicRemap);
|
|
||||||
|
|
||||||
private byte[] classicRemapStructures;
|
|
||||||
public byte[] ClassicRemapStructures => classicRemapStructures ?? (Variant == null ? null : teamColorManager.GetItem(Variant)?.ClassicRemapStructures);
|
|
||||||
|
|
||||||
private Color? lowerBounds;
|
private Color? lowerBounds;
|
||||||
public Color LowerBounds => lowerBounds.HasValue ? lowerBounds.Value : ((Variant != null) ? teamColorManager.GetItem(Variant).LowerBounds : default);
|
public Color LowerBounds => lowerBounds.HasValue ? lowerBounds.Value : ((Variant != null) ? teamColorManager.GetItem(Variant).LowerBounds : default);
|
||||||
|
|
||||||
@ -100,8 +94,6 @@ namespace MobiusEditor.Utility
|
|||||||
{
|
{
|
||||||
this.Name = col.Name;
|
this.Name = col.Name;
|
||||||
this.Variant = col.Variant;
|
this.Variant = col.Variant;
|
||||||
this.classicRemap = col.ClassicRemap;
|
|
||||||
this.classicRemapStructures = col.ClassicRemapStructures;
|
|
||||||
this.lowerBounds = col.LowerBounds;
|
this.lowerBounds = col.LowerBounds;
|
||||||
this.upperBounds = col.UpperBounds;
|
this.upperBounds = col.UpperBounds;
|
||||||
this.fudge = col.Fudge;
|
this.fudge = col.Fudge;
|
||||||
@ -128,14 +120,6 @@ namespace MobiusEditor.Utility
|
|||||||
this.radarMapColor = radarMapColor;
|
this.radarMapColor = radarMapColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Load(string name, string variant, byte[] classicRemap, byte[] classicRemapStructures)
|
|
||||||
{
|
|
||||||
this.Name = name;
|
|
||||||
this.Variant = variant;
|
|
||||||
this.classicRemap = classicRemap;
|
|
||||||
this.classicRemapStructures = classicRemapStructures;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load(string xml)
|
public void Load(string xml)
|
||||||
{
|
{
|
||||||
XmlDocument xmlDoc = new XmlDocument();
|
XmlDocument xmlDoc = new XmlDocument();
|
||||||
@ -262,17 +246,27 @@ namespace MobiusEditor.Utility
|
|||||||
|
|
||||||
public void ApplyToImage(Bitmap image, out Rectangle opaqueBounds)
|
public void ApplyToImage(Bitmap image, out Rectangle opaqueBounds)
|
||||||
{
|
{
|
||||||
|
if (image == null)
|
||||||
|
{
|
||||||
|
opaqueBounds = Rectangle.Empty;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int bytesPerPixel = Image.GetPixelFormatSize(image.PixelFormat) / 8;
|
||||||
|
if (bytesPerPixel != 3 && bytesPerPixel != 4)
|
||||||
|
{
|
||||||
|
opaqueBounds = new Rectangle(Point.Empty, image.Size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
BitmapData data = null;
|
BitmapData data = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, image.PixelFormat);
|
data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, image.PixelFormat);
|
||||||
var bytesPerPixel = Image.GetPixelFormatSize(data.PixelFormat) / 8;
|
byte[] bytes = new byte[data.Stride * data.Height];
|
||||||
var bytes = new byte[data.Stride * data.Height];
|
|
||||||
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
|
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
|
||||||
int width = data.Width;
|
int width = data.Width;
|
||||||
int height = data.Height;
|
int height = data.Height;
|
||||||
int stride = data.Stride;
|
int stride = data.Stride;
|
||||||
opaqueBounds = ImageUtils.CalculateOpaqueBounds(bytes, width, height, bytesPerPixel, stride);
|
opaqueBounds = ImageUtils.CalculateOpaqueBoundsHiCol(bytes, width, height, bytesPerPixel, stride);
|
||||||
ApplyToImage(bytes, width, height, bytesPerPixel, stride, opaqueBounds);
|
ApplyToImage(bytes, width, height, bytesPerPixel, stride, opaqueBounds);
|
||||||
Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
|
Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
|
||||||
}
|
}
|
||||||
@ -287,6 +281,11 @@ namespace MobiusEditor.Utility
|
|||||||
|
|
||||||
public void ApplyToImage(byte[] bytes, int width, int height, int bytesPerPixel, int stride, Rectangle? opaqueBounds)
|
public void ApplyToImage(byte[] bytes, int width, int height, int bytesPerPixel, int stride, Rectangle? opaqueBounds)
|
||||||
{
|
{
|
||||||
|
// Only handle 24bpp and 32bpp data.
|
||||||
|
if (bytesPerPixel != 3 && bytesPerPixel != 4)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
Rectangle bounds = opaqueBounds ?? new Rectangle(0, 0, width, height);
|
Rectangle bounds = opaqueBounds ?? new Rectangle(0, 0, width, height);
|
||||||
float frac(float x) => x - (int)x;
|
float frac(float x) => x - (int)x;
|
||||||
float lerp(float x, float y, float t) => (x * (1.0f - t)) + (y * t);
|
float lerp(float x, float y, float t) => (x * (1.0f - t)) + (y * t);
|
||||||
@ -305,8 +304,9 @@ namespace MobiusEditor.Utility
|
|||||||
for (int y = bounds.Top; y < bounds.Bottom; y++)
|
for (int y = bounds.Top; y < bounds.Bottom; y++)
|
||||||
{
|
{
|
||||||
int addr = lineStart + bounds.Left * bytesPerPixel;
|
int addr = lineStart + bounds.Left * bytesPerPixel;
|
||||||
for (int x = bounds.Left; x < width; ++x)
|
for (int x = bounds.Left; x < bounds.Right; ++x)
|
||||||
{
|
{
|
||||||
|
// 4-byte pixel = [B,G,R,A]. This code remains the same if it's 24 bpp since the alpha byte is never processed.
|
||||||
var pixel = Color.FromArgb(bytes[addr + 2], bytes[addr + 1], bytes[addr + 0]);
|
var pixel = Color.FromArgb(bytes[addr + 2], bytes[addr + 1], bytes[addr + 0]);
|
||||||
(float r, float g, float b) = (pixel.R.ToLinear(), pixel.G.ToLinear(), pixel.B.ToLinear());
|
(float r, float g, float b) = (pixel.R.ToLinear(), pixel.G.ToLinear(), pixel.B.ToLinear());
|
||||||
(float x, float y, float z, float w) K = (0.0f, -1.0f / 3.0f, 2.0f / 3.0f, -1.0f);
|
(float x, float y, float z, float w) K = (0.0f, -1.0f / 3.0f, 2.0f / 3.0f, -1.0f);
|
||||||
|
@ -44,9 +44,9 @@ The file "CnCTDRAMapEditor.exe.config" contains settings to customise the editor
|
|||||||
|
|
||||||
A mod entry can either be a Steam workshop ID, or a folder name. The paths will initially be looked up in the mods folder of the respective game in the CnCRemastered\mods\ folder under your Documents folder, but the loading system will also check the Steam workshop files for a matching mod. Sole Survivor will use Tiberian Dawn mods. Note that mods can only apply graphical changes from the tileset and house color xml files; the editor can't read any data from compiled dll files. This mods system is mostly meant to apply graphical fixes to the editor.
|
A mod entry can either be a Steam workshop ID, or a folder name. The paths will initially be looked up in the mods folder of the respective game in the CnCRemastered\mods\ folder under your Documents folder, but the loading system will also check the Steam workshop files for a matching mod. Sole Survivor will use Tiberian Dawn mods. Note that mods can only apply graphical changes from the tileset and house color xml files; the editor can't read any data from compiled dll files. This mods system is mostly meant to apply graphical fixes to the editor.
|
||||||
|
|
||||||
The **ModsToLoadTD** and **ModsToLoadSS** settings will have the `GraphicsFixesTD` mod set by default, to complete the incomplete TD Remastered graphics set, meaning the mod will automatically be loaded if found. Note that the editor has no way to check whether mods are enabled in the game, so that makes no difference.
|
The **ModsToLoadTD** and **ModsToLoadSS** settings will have the `GraphicsFixesTD` mod set by default, to complete the incomplete TD Remastered graphics set, meaning the mod will automatically be loaded if found. Similarly, the **ModsToLoadRA** setting will have the `GraphicsFixesRA` mod set. Note that the editor has no way to check whether mods are enabled in the game, so that makes no difference.
|
||||||
|
|
||||||
You can find the mod [on the Steam workshop](https://steamcommunity.com/sharedfiles/filedetails/?id=2844969675) and [on ModDB](https://www.moddb.com/games/command-conquer-remastered/addons/graphicsfixestd).
|
You can find them on the Steam workshop ([GraphicsFixesTD](https://steamcommunity.com/sharedfiles/filedetails/?id=2844969675), [GraphicsFixesRA](https://steamcommunity.com/sharedfiles/filedetails/?id=2978875641)) and on ModDB ([GraphicsFixesTD](https://www.moddb.com/games/command-conquer-remastered/addons/graphicsfixestd), [GraphicsFixesRA](https://www.moddb.com/games/cc-red-alert-remastered/addons/graphicsfixesra)).
|
||||||
|
|
||||||
### Defaults:
|
### Defaults:
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ Right now, I'm not really looking into making this a joint project. Specific bug
|
|||||||
|
|
||||||
Simply unpack the editor into a new folder on your disk somewhere. On first startup, it will automatically try to detect the folder in which the game is installed, and if it can't find it, it will show a popup asking you to locate it. Note that this autodetect only works on Steam installations of the game.
|
Simply unpack the editor into a new folder on your disk somewhere. On first startup, it will automatically try to detect the folder in which the game is installed, and if it can't find it, it will show a popup asking you to locate it. Note that this autodetect only works on Steam installations of the game.
|
||||||
|
|
||||||
It is advised to install the `GraphicsFixesTD` and `GraphicsFixesRA` mods, to fix errors and add missing bits in the Remastered graphics. The editor will use the mods automatically when they are installed, even if they are not enabled inside the game. You can find them on the Steam workshop ([GraphicsFixesTD](https://steamcommunity.com/sharedfiles/filedetails/?id=2844969675), [GraphicsFixesRA](https://steamcommunity.com/sharedfiles/filedetails/?id=2978875641)) and on ModDB ([GraphicsFixesTD](https://www.moddb.com/games/command-conquer-remastered/addons/graphicsfixestd), [GraphicsFixesRA](https://www.moddb.com/games/command-conquer-remastered/addons/graphicsfixesra)).
|
It is advised to install the `GraphicsFixesTD` and `GraphicsFixesRA` mods, to fix errors and add missing bits in the Remastered graphics. The editor will use the mods automatically when they are installed, even if they are not enabled inside the game. You can find them on the Steam workshop ([GraphicsFixesTD](https://steamcommunity.com/sharedfiles/filedetails/?id=2844969675), [GraphicsFixesRA](https://steamcommunity.com/sharedfiles/filedetails/?id=2978875641)) and on ModDB ([GraphicsFixesTD](https://www.moddb.com/games/command-conquer-remastered/addons/graphicsfixestd), [GraphicsFixesRA](https://www.moddb.com/games/cc-red-alert-remastered/addons/graphicsfixesra)).
|
||||||
|
|
||||||
**The GraphicsFixesRA mod is not currently configured in the settings file to load automatically; this will be amended in the next release. To add it manually, edit `CnCTDRAMapEditor.exe.config` and replace the `<value />` under the "ModsToLoadRA" setting with `<value>2978875641;GraphicsFixesRA</value>`**
|
**The GraphicsFixesRA mod is not currently configured in the settings file to load automatically; this will be amended in the next release. To add it manually, edit `CnCTDRAMapEditor.exe.config` and replace the `<value />` under the "ModsToLoadRA" setting with `<value>2978875641;GraphicsFixesRA</value>`**
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user