diff --git a/CnCTDRAMapEditor/Controls/ObjectProperties.cs b/CnCTDRAMapEditor/Controls/ObjectProperties.cs
index aff0532..a1ef614 100644
--- a/CnCTDRAMapEditor/Controls/ObjectProperties.cs
+++ b/CnCTDRAMapEditor/Controls/ObjectProperties.cs
@@ -138,8 +138,8 @@ namespace MobiusEditor.Controls
case Building building:
isOnMap = building.IsPrebuilt;
items = Plugin.Map.FilterStructureTriggers().Select(t => t.Name).Distinct().ToArray();
- filteredEvents = Plugin.Map.EventTypes.Where(ac => Plugin.Map.StructureEventTypes.Contains(ac)).Distinct().ToArray();
- filteredActions = Plugin.Map.ActionTypes.Where(ac => Plugin.Map.StructureActionTypes.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.BuildingActionTypes.Contains(ac)).Distinct().ToArray();
break;
default:
items = Plugin.Map.Triggers.Select(t => t.Name).Distinct().ToArray();
diff --git a/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs b/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs
index 67cb018..b011a62 100644
--- a/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs
+++ b/CnCTDRAMapEditor/Dialogs/MapSettingsDialog.cs
@@ -254,7 +254,8 @@ namespace MobiusEditor.Dialogs
briefingSettingsTracker.TryGetMember("Briefing", out object brf);
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.";
DialogResult dres = MessageBox.Show(message, "Warning", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1);
diff --git a/CnCTDRAMapEditor/Interface/ICellOccupier.cs b/CnCTDRAMapEditor/Interface/ICellOccupier.cs
index 80c11ac..cfeb494 100644
--- a/CnCTDRAMapEditor/Interface/ICellOccupier.cs
+++ b/CnCTDRAMapEditor/Interface/ICellOccupier.cs
@@ -16,6 +16,7 @@ namespace MobiusEditor.Interface
{
public interface ICellOccupier
{
+ /// Footprint of this object, determining where no other objects can be placed.
bool[,] OccupyMask { get; }
}
}
diff --git a/CnCTDRAMapEditor/Interface/ICellOverlapper.cs b/CnCTDRAMapEditor/Interface/ICellOverlapper.cs
index 7a69f3b..4656591 100644
--- a/CnCTDRAMapEditor/Interface/ICellOverlapper.cs
+++ b/CnCTDRAMapEditor/Interface/ICellOverlapper.cs
@@ -18,7 +18,9 @@ namespace MobiusEditor.Interface
{
public interface ICellOverlapper
{
+ /// Rectangular bounds of this overlapper.
Rectangle OverlapBounds { get; }
+ /// Determines for each cell whether other graphics drawn under this one are considered to be mostly visible.
bool[,] OpaqueMask { get; }
}
}
diff --git a/CnCTDRAMapEditor/Interface/IGamePlugin.cs b/CnCTDRAMapEditor/Interface/IGamePlugin.cs
index 0c593ee..764ecad 100644
--- a/CnCTDRAMapEditor/Interface/IGamePlugin.cs
+++ b/CnCTDRAMapEditor/Interface/IGamePlugin.cs
@@ -38,42 +38,113 @@ namespace MobiusEditor.Interface
public interface IGamePlugin : IDisposable
{
+ /// Name of the game.
string Name { get; }
+ /// The game type as enum.
GameType GameType { get; }
+ /// True if the plugin is initialised to handle a megamap.
bool IsMegaMap { get; }
+ /// The map.
Map Map { get; }
+ /// The map image to show.
Image MapImage { get; }
+ /// Feedback handler that can be attached to the plugin.
IFeedBackHandler FeedBackHandler { get; set; }
+ /// True if the currently loaded map was modified.
bool Dirty { get; set; }
+ /// Extra ini text that can be freely edited by the user.
string ExtraIniText { get; set; }
+ ///
+ /// Create a new map in the chosen theater.
+ ///
+ /// The name of the theater to use.
void New(string theater);
+ ///
+ /// Load a map.
+ ///
+ /// Path of the map to load.
+ /// File type of the actual file in the path, so accompanying files can be loaded correctly.
+ /// Any issues encountered when loading the map.
IEnumerable Load(string path, FileType fileType);
+ ///
+ /// Save the current map to the given path, with the given file type.
+ ///
+ /// Path of the map to save.
+ /// File type of the actual file in the path, so accompanying files can be saved correctly.
+ /// true if the saving succeeded.
bool Save(string path, FileType fileType);
+ ///
+ /// Save the current map to the given path, with the given file type.
+ ///
+ /// Path of the map to save.
+ /// File type of the actual file in the path, so accompanying files can be saved correctly.
+ /// Custom preview given to the map.
+ /// True to not resave the preview on disc when doing the save operation.
+ /// true if the saving succeeded.
bool Save(string path, FileType fileType, Bitmap customPreview, bool dontResavePreview);
+ ///
+ /// Validate the map to see if there are any blocking errors preventing it from saving.
+ ///
+ /// true if the validation succeeded.
string Validate();
+ ///
+ /// Generates an overview of how many items are on the map and how many are allowed, and does a trigger analysis.
+ ///
+ /// The generated map items overview.
IEnumerable AssessMapItems();
+ ///
+ /// Retrieves a hash set of all houses for which production is started by triggers.
+ ///
+ /// A hash set of all houses for which production is started by triggers.
HashSet GetHousesWithProduction();
+ ///
+ /// Returns an array containing the reveal radius for each waypoint on the map.
+ ///
+ /// The map to gheck the waypoints on.
+ /// False for small flare reveal, true for large area reveal.
+ ///
int[] GetRevealRadiusForWaypoints(Map map, bool forLargeReveal);
+ ///
+ /// Check whether there are any errors in the currect scripting.
+ ///
+ /// List of triggers to check.
+ /// True to fetch extra data from the map, such as map objects that triggers can be linked to.
+ /// 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.
+ /// True to report fatal issues only.
+ /// Returns true if fatal issues were encountered.
+ /// True to fix issues that are encountered whenever possible.
+ /// Returns true if issues were fixed.
+ /// A summation of the encountered issues.
IEnumerable CheckTriggers(IEnumerable triggers, bool includeExternalData, bool prefixNames, bool fatalOnly, out bool fatal, bool fix, out bool wasFixed);
+ ///
+ /// Checks whether the name has a default map name that is considered empty by this game plugin.
+ ///
+ /// Map name to check.
+ /// True if the given name is considered empty by this game plugin.
bool MapNameIsEmpty(string name);
- Boolean EvaluateBriefing(string briefing, out string message);
+ ///
+ /// Checks whether the briefing has any kind of issues concerning length or supported characters.
+ ///
+ /// The briefing to check
+ /// Null if everything is okay, otherwise any issues to show on the user interface.
+ string EvaluateBriefing(string briefing);
}
}
diff --git a/CnCTDRAMapEditor/Interface/ITeamColor.cs b/CnCTDRAMapEditor/Interface/ITeamColor.cs
index 7d39c18..42442bb 100644
--- a/CnCTDRAMapEditor/Interface/ITeamColor.cs
+++ b/CnCTDRAMapEditor/Interface/ITeamColor.cs
@@ -12,10 +12,22 @@ namespace MobiusEditor.Interface
{
public interface ITeamColor
{
+ /// Name of the color, used as identifier.
string Name { get; }
+ /// A general color representing this team color.
Color BaseColor { get; }
+
+ ///
+ /// Apply this color to a given image.
+ ///
+ /// The image to process.
void ApplyToImage(Bitmap image);
+
+ ///
+ /// Apply this color to a given image, and output the actually-used bounds of the graphics.
+ ///
+ /// The image to process.
+ /// The actually-used nontransparent area of the graphics that need to be painted.
void ApplyToImage(Bitmap image, out Rectangle opaqueBounds);
- void ApplyToImage(byte[] bytes, int width, int height, int bytesPerPixel, int stride, Rectangle? opaqueBounds);
}
}
\ No newline at end of file
diff --git a/CnCTDRAMapEditor/Interface/ITechnoType.cs b/CnCTDRAMapEditor/Interface/ITechnoType.cs
index 950fe21..41fb40d 100644
--- a/CnCTDRAMapEditor/Interface/ITechnoType.cs
+++ b/CnCTDRAMapEditor/Interface/ITechnoType.cs
@@ -16,11 +16,17 @@ namespace MobiusEditor.Interface
{
public interface ITechnoType: IBrowsableType
{
+ /// Object ID
sbyte ID { get; }
+ /// Object ini name
string Name { get; }
+ /// True if this object has a weapon. This affects the default orders for placing it on the map.
bool IsArmed { get; }
+ /// True if this object is an aircraft, and is normally not placeable on the map.
bool IsAircraft { get; }
+ /// 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.
bool IsFixedWing { get; }
+ /// True if this object can harvest resources. This affects the default orders for placing it on the map.
bool IsHarvester { get; }
}
}
diff --git a/CnCTDRAMapEditor/Model/BuildingType.cs b/CnCTDRAMapEditor/Model/BuildingType.cs
index 40fecb0..5796b2f 100644
--- a/CnCTDRAMapEditor/Model/BuildingType.cs
+++ b/CnCTDRAMapEditor/Model/BuildingType.cs
@@ -24,14 +24,23 @@ namespace MobiusEditor.Model
[Flags]
public enum BuildingTypeFlag
{
+ /// No flags set.
None = 0,
+ /// Produces structures.
Factory = (1 << 0),
+ /// Has a bib attached.
Bib = (1 << 1),
+ /// Is a fake building.
Fake = (1 << 2),
+ /// Has a rotating turret, and accepts a Facing value in the ini file.
Turret = (1 << 3),
+ /// Only has a single frame of graphics.
SingleFrame = (1 << 4),
+ /// Does not adjust to house colors.
NoRemap = (1 << 5),
+ /// Is flat on the ground; anything around will overlap this building's graphics.
Flat = (1 << 6),
+ /// Can show a gap area-of-effect radius indicator.
IsGapGenerator = (1 << 7),
}
@@ -70,7 +79,8 @@ namespace MobiusEditor.Model
get { return (Flag & BuildingTypeFlag.Bib) == BuildingTypeFlag.Bib; }
set
{
- if (value)
+ // Bibs are only supported for widths 2 to 4
+ if (value && Size.Width >= 2 && Size.Width <= 4)
{
Flag |= BuildingTypeFlag.Bib;
}
@@ -118,7 +128,8 @@ namespace MobiusEditor.Model
this.OwnerHouse = ownerHouse;
this.Theaters = theaters;
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)
diff --git a/CnCTDRAMapEditor/Model/Map.cs b/CnCTDRAMapEditor/Model/Map.cs
index e621ae6..c2104be 100644
--- a/CnCTDRAMapEditor/Model/Map.cs
+++ b/CnCTDRAMapEditor/Model/Map.cs
@@ -79,14 +79,14 @@ namespace MobiusEditor.Model
public PropertyDescriptor PropertyDescriptor { get; private set; }
- public Map Map => Instance as Map;
+ public Map Map => this.Instance as Map;
public readonly bool FractionalPercentages;
public MapContext(Map map, bool fractionalPercentages)
{
- Instance = map;
- FractionalPercentages = fractionalPercentages;
+ this.Instance = map;
+ this.FractionalPercentages = fractionalPercentages;
}
public object GetService(Type serviceType) => null;
@@ -168,24 +168,24 @@ namespace MobiusEditor.Model
public readonly SteamSection SteamSection = new SteamSection();
- public TheaterType Theater { get => MapSection.Theater; set => MapSection.Theater = value; }
+ public TheaterType Theater { get => this.MapSection.Theater; set => this.MapSection.Theater = value; }
public Point TopLeft
{
- get => new Point(MapSection.X, MapSection.Y);
- set { MapSection.X = value.X; MapSection.Y = value.Y; }
+ get => new Point(this.MapSection.X, this.MapSection.Y);
+ set { this.MapSection.X = value.X; this.MapSection.Y = value.Y; }
}
public Size Size
{
- get => new Size(MapSection.Width, MapSection.Height);
- set { MapSection.Width = value.Width; MapSection.Height = value.Height; }
+ get => new Size(this.MapSection.Width, this.MapSection.Height);
+ set { this.MapSection.Width = value.Width; this.MapSection.Height = value.Height; }
}
public Rectangle Bounds
{
- get => new Rectangle(TopLeft, Size);
- set { MapSection.X = value.Left; MapSection.Y = value.Top; MapSection.Width = value.Width; MapSection.Height = value.Height; }
+ get => new Rectangle(this.TopLeft, this.Size);
+ set { this.MapSection.X = value.Left; this.MapSection.Y = value.Top; this.MapSection.Width = value.Width; this.MapSection.Height = value.Height; }
}
public readonly Type HouseType;
@@ -209,13 +209,13 @@ namespace MobiusEditor.Model
public readonly string[] EventTypes;
public readonly HashSet CellEventTypes;
public readonly HashSet UnitEventTypes;
- public readonly HashSet StructureEventTypes;
+ public readonly HashSet BuildingEventTypes;
public readonly HashSet TerrainEventTypes;
public readonly string[] ActionTypes;
public readonly HashSet CellActionTypes;
public readonly HashSet UnitActionTypes;
- public readonly HashSet StructureActionTypes;
+ public readonly HashSet BuildingActionTypes;
public readonly HashSet TerrainActionTypes;
public readonly string[] MissionTypes;
@@ -240,11 +240,11 @@ namespace MobiusEditor.Model
{
get
{
- if (BasicSection == null || !BasicSection.ExpansionEnabled)
+ if (this.BasicSection == null || !this.BasicSection.ExpansionEnabled)
{
- return AllInfantryTypes.Where(inf => !inf.IsExpansionUnit).ToList();
+ return this.AllInfantryTypes.Where(inf => !inf.IsExpansionUnit).ToList();
}
- return AllInfantryTypes.ToList();
+ return this.AllInfantryTypes.ToList();
}
}
@@ -253,11 +253,11 @@ namespace MobiusEditor.Model
{
get
{
- if (BasicSection == null || !BasicSection.ExpansionEnabled)
+ if (this.BasicSection == null || !this.BasicSection.ExpansionEnabled)
{
- return AllUnitTypes.Where(un => !un.IsExpansionUnit).ToList();
+ return this.AllUnitTypes.Where(un => !un.IsExpansionUnit).ToList();
}
- return AllUnitTypes.ToList();
+ return this.AllUnitTypes.ToList();
}
}
@@ -268,11 +268,11 @@ namespace MobiusEditor.Model
{
get
{
- if (BasicSection == null || !BasicSection.ExpansionEnabled)
+ if (this.BasicSection == null || !this.BasicSection.ExpansionEnabled)
{
- return AllTeamTechnoTypes.Where(tc => (tc is UnitType ut && !ut.IsExpansionUnit) || (tc is InfantryType it && !it.IsExpansionUnit)).ToList();
+ return this.AllTeamTechnoTypes.Where(tc => (tc is UnitType ut && !ut.IsExpansionUnit) || (tc is InfantryType it && !it.IsExpansionUnit)).ToList();
}
- return AllTeamTechnoTypes.ToList();
+ return this.AllTeamTechnoTypes.ToList();
}
}
@@ -321,14 +321,14 @@ namespace MobiusEditor.Model
private List triggers;
public List Triggers
{
- get { return triggers; }
+ get { return this.triggers; }
set
{
- triggers = value;
+ this.triggers = value;
// Only an actual replacing of the list will call these, but they can be called manually after an update.
// A bit more manual than the whole ObservableCollection system, but a lot less cumbersome.
//CleanUpTriggers();
- NotifyTriggersUpdate();
+ this.NotifyTriggersUpdate();
}
}
@@ -343,23 +343,248 @@ namespace MobiusEditor.Model
public readonly string ThemeEmpty;
public readonly List ThemeTypes;
+ ///The value for the basic resource on the map.
public int TiberiumOrGoldValue { get; set; }
+ /// The value for the high-value resource on the map.
public int GemValue { get; set; }
- public int TotalResources
+ /// Gets the total amount of resources on the map inside the map border.
+ public int ResourcesOnMap
{
get
{
- return GetTotalResources(false);
+ return this.GetTotalResources(true);
}
}
- public int ResourcesInBounds
+ ///
+ /// Initialises a new Map object.
+ ///
+ /// ini [Basic] section in the specific object type for this game.
+ /// Theater of the map.
+ /// Size of the map, in cells.
+ /// Type to use for creating House objects from houseTypes.
+ /// The list of house types.
+ /// The list of team colors to use for multiplayer start location indicators.
+ /// The list of all theaters supported by this game.
+ /// The list of all template types supported by this game.
+ /// The list of all terrain types supported by this game.
+ /// The list of all overlay types supported by this game.
+ /// The list of all smudge types supported by this game.
+ /// The list of all event types supported by this game.
+ /// The list of all event types applicable to cells in this game.
+ /// The list of all event types applicable to units in this game.
+ /// The list of all event types applicable to buildings in this game.
+ /// The list of all event types applicable to terrain in this game.
+ /// The list of all action types supported by this game.
+ /// The list of all action types applicable to cells in this game.
+ /// The list of all action types applicable to units in this game.
+ /// The list of all action types applicable to buildings in this game.
+ /// The list of all action types applicable to terrain in this game.
+ /// The list of all mission types (orders) supported by this game.
+ /// The default mission for armed units.
+ /// The default mission for unarmed units.
+ /// The default mission for harvesting units.
+ /// The default mission for air units.
+ /// The list of all direction types applicable to units.
+ /// The list of all direction types applicable to structures.
+ /// The list of all infantry types.
+ /// The list of all unit types.
+ /// The list of all building types.
+ /// The list of all mission types (orders) usable by teams.
+ /// The list of all techno types usable in teams.
+ /// The list of waypoints.
+ /// The radius that is revealed around a dropzone waypoint.
+ /// The radius that is affected by a gap generator.
+ /// The radius that is affected by a radar jammer.
+ /// The list of all movies usable by this game.
+ /// The name to use for detecting and saving an empty movie entry.
+ /// The list of all music usable by this game.
+ /// The name to use for detecting and saving an empty music entry.
+ /// The value for the basic resource on the map.
+ /// The value for the high-value resource on the map.
+ public Map(BasicSection basicSection, TheaterType theater, Size cellSize, Type houseType, IEnumerable houseTypes,
+ IEnumerable flagColors, IEnumerable theaterTypes, IEnumerable templateTypes,
+ IEnumerable terrainTypes, IEnumerable overlayTypes, IEnumerable smudgeTypes,
+ IEnumerable eventTypes, IEnumerable cellEventTypes, IEnumerable unitEventTypes, IEnumerable buildingEventTypes, IEnumerable terrainEventTypes,
+ IEnumerable actionTypes, IEnumerable cellActionTypes, IEnumerable unitActionTypes, IEnumerable buildingActionTypes, IEnumerable terrainActionTypes,
+ IEnumerable missionTypes, string armedMission, string unarmedMission, string harvestMission, string aircraftMission,
+ IEnumerable unitDirectionTypes, IEnumerable buildingDirectionTypes, IEnumerable infantryTypes,
+ IEnumerable unitTypes, IEnumerable buildingTypes, IEnumerable teamMissionTypes, IEnumerable teamTechnoTypes,
+ IEnumerable waypoints, int dropZoneRadius, int gapRadius, int jamRadius, IEnumerable movieTypes, string emptyMovie, IEnumerable themeTypes, string emptyTheme,
+ int tiberiumOrGoldValue, int gemValue)
{
- get
+ this.MapSection = new MapSection(cellSize);
+ this.BasicSection = basicSection;
+ this.HouseType = houseType;
+ this.HouseTypesIncludingNone = houseTypes.ToArray();
+ this.HouseTypes = this.HouseTypesIncludingNone.Where(h => h.ID >= 0).ToArray();
+ this.FlagColors = flagColors.ToArray();
+ this.TheaterTypes = new List(theaterTypes);
+ this.TemplateTypes = new List(templateTypes);
+ this.TerrainTypes = new List(terrainTypes);
+ this.OverlayTypes = new List(overlayTypes);
+ this.SmudgeTypes = new List(smudgeTypes);
+ this.EventTypes = eventTypes.ToArray();
+ this.CellEventTypes = cellEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+ this.UnitEventTypes = unitEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+ this.BuildingEventTypes = buildingEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+ this.TerrainEventTypes = terrainEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+ this.CellActionTypes = cellActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+ this.UnitActionTypes = unitActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+ this.BuildingActionTypes = buildingActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+ this.TerrainActionTypes = terrainActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
+
+ this.ActionTypes = actionTypes.ToArray();
+ this.MissionTypes = missionTypes.ToArray();
+ string defMission = this.MissionTypes.Where(m => m.Equals(defaultMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? this.MissionTypes.First();
+ // Unfiltered originals, to ensure this remains correct when cloning.
+ this.inputMissionArmed = armedMission;
+ this.inputMissionUnarmed = unarmedMission;
+ this.inputMissionAircraft = harvestMission;
+ this.inputMissionHarvest = aircraftMission;
+
+ this.DefaultMissionArmed = this.MissionTypes.Where(m => m.Equals(armedMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? defMission;
+ this.DefaultMissionUnarmed = this.MissionTypes.Where(m => m.Equals(unarmedMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? defMission;
+ // Reverts to "Stop" if there are no resources (RA indoor)
+ this.DefaultMissionHarvest = this.OverlayTypes.Any(ov => ov.IsResource) ? this.MissionTypes.Where(m => m.Equals(harvestMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? this.DefaultMissionUnarmed : this.DefaultMissionUnarmed;
+ // Only "Unload" will make them stay on the spot as expected.
+ this.DefaultMissionAircraft = this.MissionTypes.Where(m => m.Equals(aircraftMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? defMission;
+ this.UnitDirectionTypes = new List(unitDirectionTypes);
+ this.BuildingDirectionTypes = new List(buildingDirectionTypes);
+ this.AllInfantryTypes = new List(infantryTypes);
+ this.AllUnitTypes = new List(unitTypes);
+ this.BuildingTypes = new List(buildingTypes);
+ this.TeamMissionTypes = teamMissionTypes.ToArray();
+ this.AllTeamTechnoTypes = new List(teamTechnoTypes);
+ this.MovieEmpty = emptyMovie;
+ this.MovieTypes = new List(movieTypes);
+ this.ThemeEmpty = emptyTheme;
+ this.ThemeTypes = new List(themeTypes);
+ this.TiberiumOrGoldValue = tiberiumOrGoldValue;
+ this.GemValue = gemValue;
+ this.Metrics = new CellMetrics(cellSize);
+ this.Templates = new CellGrid(this.Metrics);
+ this.Overlay = new CellGrid(this.Metrics);
+ this.Smudge = new CellGrid(this.Metrics);
+ this.Technos = new OccupierSet(this.Metrics);
+ this.Buildings = new OccupierSet(this.Metrics);
+ this.Overlappers = new OverlapperSet(this.Metrics);
+ this.triggers = new List();
+ this.TeamTypes = new List();
+ this.HousesIncludingNone = this.HouseTypesIncludingNone.Select(t => { var h = (House)Activator.CreateInstance(this.HouseType, t); h.SetDefault(); return h; }).ToArray();
+ this.Houses = this.HousesIncludingNone.Where(h => h.Type.ID >= 0).ToArray();
+ this.Waypoints = waypoints.ToArray();
+ for (int i = 0; i < this.Waypoints.Length; ++i)
{
- return GetTotalResources(true);
+ // Deep clone, with current metric to allow showing waypoints as cell coordinates.
+ this.Waypoints[i] = new Waypoint(this.Waypoints[i].Name, this.Waypoints[i].Flag, this.Metrics, this.Waypoints[i].Cell);
}
+ this.DropZoneRadius = dropZoneRadius;
+ this.GapRadius = gapRadius;
+ this.RadarJamRadius = jamRadius;
+ this.CellTriggers = new CellGrid(this.Metrics);
+
+ this.MapSection.SetDefault();
+ this.BriefingSection.SetDefault();
+ this.SteamSection.SetDefault();
+ this.Templates.Clear();
+ this.Overlay.Clear();
+ this.Smudge.Clear();
+ this.Technos.Clear();
+ this.Overlappers.Clear();
+ this.CellTriggers.Clear();
+
+ this.TopLeft = new Point(1, 1);
+ this.Size = this.Metrics.Size - new Size(2, 2);
+ this.Theater = theater;
+
+ this.Overlay.CellChanged += this.Overlay_CellChanged;
+ this.Technos.OccupierAdded += this.Technos_OccupierAdded;
+ this.Technos.OccupierRemoved += this.Technos_OccupierRemoved;
+ this.Buildings.OccupierAdded += this.Buildings_OccupierAdded;
+ this.Buildings.OccupierRemoved += this.Buildings_OccupierRemoved;
+ }
+
+ public void BeginUpdate()
+ {
+ this.updateCount++;
+ }
+
+ public void EndUpdate()
+ {
+ if (--this.updateCount == 0)
+ {
+ this.Update();
+ }
+ }
+
+ public void InitTheater(GameType gameType)
+ {
+ foreach (var templateType in this.TemplateTypes.Where(itm => itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
+ {
+ templateType.Init(this.Theater);
+ }
+ foreach (var smudgeType in this.SmudgeTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
+ {
+ smudgeType.Init(this.Theater);
+ }
+ foreach (var overlayType in this.OverlayTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
+ {
+ overlayType.Init(gameType, this.Theater);
+ }
+ foreach (var terrainType in this.TerrainTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
+ {
+ terrainType.Init(this.Theater);
+ }
+ // Ignore expansion status for these; they can still be enabled later.
+ DirectionType infDir = this.UnitDirectionTypes.Where(d => d.Facing == FacingType.South).First();
+ foreach (var infantryType in this.AllInfantryTypes)
+ {
+ infantryType.Init(gameType, this.Theater, this.HouseTypesIncludingNone.Where(h => h.Equals(infantryType.OwnerHouse)).FirstOrDefault(), infDir);
+ }
+ DirectionType unitDir = this.UnitDirectionTypes.Where(d => d.Facing == FacingType.SouthWest).First();
+ foreach (var unitType in this.AllUnitTypes)
+ {
+ unitType.Init(gameType, this.Theater, this.HouseTypesIncludingNone.Where(h => h.Equals(unitType.OwnerHouse)).FirstOrDefault(), unitDir);
+ }
+ DirectionType bldDir = this.UnitDirectionTypes.Where(d => d.Facing == FacingType.North).First();
+ foreach (var buildingType in this.BuildingTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(this.Theater)))
+ {
+ buildingType.Init(gameType, this.Theater, this.HouseTypesIncludingNone.Where(h => h.Equals(buildingType.OwnerHouse)).FirstOrDefault(), bldDir);
+ }
+ }
+
+ private void Update()
+ {
+ this.updating = true;
+ if (this.invalidateLayers.TryGetValue(MapLayerFlag.Resources, out ISet locations))
+ {
+ this.UpdateResourceOverlays(locations, true);
+ }
+ if (this.invalidateLayers.TryGetValue(MapLayerFlag.Walls, out locations))
+ {
+ this.UpdateWallOverlays(locations);
+ }
+ if (this.invalidateLayers.TryGetValue(MapLayerFlag.Overlay, out locations))
+ {
+ this.UpdateConcreteOverlays(locations);
+ //UpdateConcreteOverlays_ORIG(locations);
+ }
+ if (this.invalidateOverlappers)
+ {
+ this.Overlappers.Clear();
+ foreach (var (location, techno) in this.Technos)
+ {
+ if (techno is ICellOverlapper)
+ {
+ this.Overlappers.Add(location, techno as ICellOverlapper);
+ }
+ }
+ }
+ this.invalidateLayers.Clear();
+ this.invalidateOverlappers = false;
+ this.updating = false;
}
///
@@ -371,10 +596,10 @@ namespace MobiusEditor.Model
private int GetTotalResources(bool inBounds)
{
int totalResources = 0;
- foreach (var (cell, value) in Overlay)
+ foreach (var (cell, value) in this.Overlay)
{
Point point;
- if (!value.Type.IsResource || !Metrics.GetLocation(cell, out point))
+ if (!value.Type.IsResource || !this.Metrics.GetLocation(cell, out point))
{
continue;
}
@@ -386,8 +611,8 @@ namespace MobiusEditor.Model
foreach (FacingType facing in CellMetrics.AdjacentFacings)
{
Overlay ovl;
- if (Metrics.Adjacent(point, facing, out Point adjPoint)
- && (ovl = Overlay[adjPoint]) != null && ovl.Type.IsResource)
+ if (this.Metrics.Adjacent(point, facing, out Point adjPoint)
+ && (ovl = this.Overlay[adjPoint]) != null && ovl.Type.IsResource)
{
if (inBounds && !this.Bounds.Contains(adjPoint))
{
@@ -402,207 +627,31 @@ namespace MobiusEditor.Model
// Harvesting one gem stage fills one bail, plus 3 extra bails. Last stage is 0 (due to a bug), but still gets the extra bails.
if (Globals.ApplyHarvestBug)
{
- totalResources += value.Type.IsGem ? thickness * GemValue * 4 + GemValue * 3 : thickness * TiberiumOrGoldValue;
+ totalResources += value.Type.IsGem ? thickness * this.GemValue * 4 + this.GemValue * 3 : thickness * this.TiberiumOrGoldValue;
}
else
{
// Fixed logic, in case it is repaired in the code.
- totalResources += (thickness + 1) * (value.Type.IsGem ? GemValue * 4 : TiberiumOrGoldValue);
+ totalResources += (thickness + 1) * (value.Type.IsGem ? this.GemValue * 4 : this.TiberiumOrGoldValue);
}
}
return totalResources;
}
- public Map(BasicSection basicSection, TheaterType theater, Size cellSize, Type houseType, IEnumerable houseTypes,
- IEnumerable flagColors, IEnumerable theaterTypes, IEnumerable templateTypes,
- IEnumerable terrainTypes, IEnumerable overlayTypes, IEnumerable smudgeTypes,
- IEnumerable eventTypes, IEnumerable cellEventTypes, IEnumerable unitEventTypes, IEnumerable structureEventTypes, IEnumerable terrainEventTypes,
- IEnumerable actionTypes, IEnumerable cellActionTypes, IEnumerable unitActionTypes, IEnumerable structureActionTypes, IEnumerable terrainActionTypes,
- IEnumerable missionTypes, string armedMission, string unarmedMission, string harvestMission, string aircraftMission,
- IEnumerable unitDirectionTypes, IEnumerable buildingDirectionTypes, IEnumerable infantryTypes,
- IEnumerable unitTypes, IEnumerable buildingTypes, IEnumerable teamMissionTypes, IEnumerable teamTechnoTypes,
- IEnumerable waypoints, int dropZoneRadius, int gapRadius, int jamRadius, IEnumerable movieTypes, string emptyMovie, IEnumerable themeTypes, string emptyTheme,
- int tiberiumOrGoldValue, int gemValue)
- {
- MapSection = new MapSection(cellSize);
- BasicSection = basicSection;
- HouseType = houseType;
- HouseTypesIncludingNone = houseTypes.ToArray();
- HouseTypes = HouseTypesIncludingNone.Where(h => h.ID >= 0).ToArray();
- FlagColors = flagColors.ToArray();
- TheaterTypes = new List(theaterTypes);
- TemplateTypes = new List(templateTypes);
- TerrainTypes = new List(terrainTypes);
- OverlayTypes = new List(overlayTypes);
- SmudgeTypes = new List(smudgeTypes);
- EventTypes = eventTypes.ToArray();
- CellEventTypes = cellEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
- UnitEventTypes = unitEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
- StructureEventTypes = structureEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
- TerrainEventTypes = terrainEventTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
- CellActionTypes = cellActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
- UnitActionTypes = unitActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
- StructureActionTypes = structureActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
- TerrainActionTypes = terrainActionTypes.ToHashSet(StringComparer.OrdinalIgnoreCase);
-
- ActionTypes = actionTypes.ToArray();
- MissionTypes = missionTypes.ToArray();
- string defMission = MissionTypes.Where(m => m.Equals(defaultMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? MissionTypes.First();
- inputMissionArmed = armedMission;
- inputMissionUnarmed = unarmedMission;
- inputMissionAircraft = harvestMission;
- inputMissionHarvest = aircraftMission;
- DefaultMissionArmed = MissionTypes.Where(m => m.Equals(armedMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? defMission;
- DefaultMissionUnarmed = MissionTypes.Where(m => m.Equals(unarmedMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? defMission;
- // Reverts to "Stop" if there are no resources (RA indoor)
- DefaultMissionHarvest = OverlayTypes.Any(ov => ov.IsResource) ? MissionTypes.Where(m => m.Equals(harvestMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? DefaultMissionUnarmed : DefaultMissionUnarmed;
- // Only "Unload" will make them stay on the spot as expected.
- DefaultMissionAircraft = MissionTypes.Where(m => m.Equals(aircraftMission, StringComparison.OrdinalIgnoreCase)).FirstOrDefault() ?? defMission;
- UnitDirectionTypes = new List(unitDirectionTypes);
- BuildingDirectionTypes = new List(buildingDirectionTypes);
- AllInfantryTypes = new List(infantryTypes);
- AllUnitTypes = new List(unitTypes);
- BuildingTypes = new List(buildingTypes);
- TeamMissionTypes = teamMissionTypes.ToArray();
- AllTeamTechnoTypes = new List(teamTechnoTypes);
- MovieEmpty = emptyMovie;
- MovieTypes = new List(movieTypes);
- ThemeEmpty = emptyTheme;
- ThemeTypes = new List(themeTypes);
- TiberiumOrGoldValue = tiberiumOrGoldValue;
- GemValue = gemValue;
- Metrics = new CellMetrics(cellSize);
- Templates = new CellGrid(Metrics);
- Overlay = new CellGrid(Metrics);
- Smudge = new CellGrid(Metrics);
- Technos = new OccupierSet(Metrics);
- Buildings = new OccupierSet(Metrics);
- Overlappers = new OverlapperSet(Metrics);
- triggers = new List();
- TeamTypes = new List();
- HousesIncludingNone = HouseTypesIncludingNone.Select(t => { var h = (House)Activator.CreateInstance(HouseType, t); h.SetDefault(); return h; }).ToArray();
- Houses = HousesIncludingNone.Where(h => h.Type.ID >= 0).ToArray();
- Waypoints = waypoints.ToArray();
- for (int i = 0; i < Waypoints.Length; ++i)
- {
- // Deep clone, with current metric to allow showing waypoints as cell coordinates.
- Waypoints[i] = new Waypoint(Waypoints[i].Name, Waypoints[i].Flag, Metrics, Waypoints[i].Cell);
- }
- DropZoneRadius = dropZoneRadius;
- GapRadius = gapRadius;
- RadarJamRadius = jamRadius;
- CellTriggers = new CellGrid(Metrics);
-
- MapSection.SetDefault();
- BriefingSection.SetDefault();
- SteamSection.SetDefault();
- Templates.Clear();
- Overlay.Clear();
- Smudge.Clear();
- Technos.Clear();
- Overlappers.Clear();
- CellTriggers.Clear();
-
- TopLeft = new Point(1, 1);
- Size = Metrics.Size - new Size(2, 2);
- Theater = theater;
-
- Overlay.CellChanged += Overlay_CellChanged;
- Technos.OccupierAdded += Technos_OccupierAdded;
- Technos.OccupierRemoved += Technos_OccupierRemoved;
- Buildings.OccupierAdded += Buildings_OccupierAdded;
- Buildings.OccupierRemoved += Buildings_OccupierRemoved;
- }
-
- public void BeginUpdate()
- {
- updateCount++;
- }
-
- public void EndUpdate()
- {
- if (--updateCount == 0)
- {
- Update();
- }
- }
-
- public void InitTheater(GameType gameType)
- {
- foreach (var templateType in TemplateTypes.Where(itm => itm.Theaters == null || itm.Theaters.Contains(Theater)))
- {
- templateType.Init(Theater);
- }
- foreach (var smudgeType in SmudgeTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(Theater)))
- {
- smudgeType.Init(Theater);
- }
- foreach (var overlayType in OverlayTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(Theater)))
- {
- overlayType.Init(gameType, Theater);
- }
- foreach (var terrainType in TerrainTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(Theater)))
- {
- terrainType.Init(Theater);
- }
- // Ignore expansion status for these; they can still be enabled later.
- DirectionType infDir = UnitDirectionTypes.Where(d => d.Facing == FacingType.South).First();
- foreach (var infantryType in AllInfantryTypes)
- {
- infantryType.Init(gameType, Theater, HouseTypesIncludingNone.Where(h => h.Equals(infantryType.OwnerHouse)).FirstOrDefault(), infDir);
- }
- DirectionType unitDir = UnitDirectionTypes.Where(d => d.Facing == FacingType.SouthWest).First();
- foreach (var unitType in AllUnitTypes)
- {
- unitType.Init(gameType, Theater, HouseTypesIncludingNone.Where(h => h.Equals(unitType.OwnerHouse)).FirstOrDefault(), unitDir);
- }
- DirectionType bldDir = UnitDirectionTypes.Where(d => d.Facing == FacingType.North).First();
- foreach (var buildingType in BuildingTypes.Where(itm => !Globals.FilterTheaterObjects || itm.Theaters == null || itm.Theaters.Contains(Theater)))
- {
- buildingType.Init(gameType, Theater, HouseTypesIncludingNone.Where(h => h.Equals(buildingType.OwnerHouse)).FirstOrDefault(), bldDir);
- }
- }
-
- private void Update()
- {
- updating = true;
- if (invalidateLayers.TryGetValue(MapLayerFlag.Resources, out ISet locations))
- {
- UpdateResourceOverlays(locations, true);
- }
- if (invalidateLayers.TryGetValue(MapLayerFlag.Walls, out locations))
- {
- UpdateWallOverlays(locations);
- }
- if (invalidateLayers.TryGetValue(MapLayerFlag.Overlay, out locations))
- {
- UpdateConcreteOverlays(locations);
- //UpdateConcreteOverlays_ORIG(locations);
- }
- if (invalidateOverlappers)
- {
- Overlappers.Clear();
- foreach (var (location, techno) in Technos)
- {
- if (techno is ICellOverlapper)
- {
- Overlappers.Add(location, techno as ICellOverlapper);
- }
- }
- }
- invalidateLayers.Clear();
- invalidateOverlappers = false;
- updating = false;
- }
-
+ ///
+ /// Update resource overlay to the desired density and randomised type.
+ ///
+ /// Set of Locations on which changes occurred.
+ /// True if resources out of bounds are reduced to minimum size and marked to be tinted red.
+ /// This function is separate from GetTotalResources because it only updates the specified areas.
public void UpdateResourceOverlays(ISet locations, bool reduceOutOfBounds)
{
Rectangle checkBounds = reduceOutOfBounds ? this.Bounds : this.Metrics.Bounds;
- OverlayType[] tiberiumOrGoldTypes = OverlayTypes.Where(t => t.IsTiberiumOrGold).ToArray();
+ OverlayType[] tiberiumOrGoldTypes = this.OverlayTypes.Where(t => t.IsTiberiumOrGold).ToArray();
if (tiberiumOrGoldTypes.Length == 0) tiberiumOrGoldTypes = null;
- OverlayType[] gemTypes = OverlayTypes.Where(t => t.IsGem).ToArray();
+ OverlayType[] gemTypes = this.OverlayTypes.Where(t => t.IsGem).ToArray();
if (gemTypes.Length == 0) gemTypes = null;
- foreach (var (location, overlay) in Overlay.IntersectsWithPoints(locations).Where(o => o.Value.Type.IsResource))
+ foreach (var (location, overlay) in this.Overlay.IntersectsWithPoints(locations).Where(o => o.Value.Type.IsResource))
{
int count = 0;
bool inBounds = checkBounds.Contains(location);
@@ -610,9 +659,9 @@ namespace MobiusEditor.Model
{
foreach (var facing in CellMetrics.AdjacentFacings)
{
- if (Metrics.Adjacent(location, facing, out Point adjacent) && checkBounds.Contains(adjacent))
+ if (this.Metrics.Adjacent(location, facing, out Point adjacent) && checkBounds.Contains(adjacent))
{
- var adjacentOverlay = Overlay[adjacent];
+ var adjacentOverlay = this.Overlay[adjacent];
if (adjacentOverlay?.Type.IsResource ?? false)
{
count++;
@@ -636,12 +685,12 @@ namespace MobiusEditor.Model
private void UpdateWallOverlays(ISet locations)
{
- foreach (var (location, overlay) in Overlay.IntersectsWithPoints(locations).Where(o => o.Value.Type.IsWall))
+ foreach (var (location, overlay) in this.Overlay.IntersectsWithPoints(locations).Where(o => o.Value.Type.IsWall))
{
- Overlay northWall = Overlay.Adjacent(location, FacingType.North);
- Overlay eastWall = Overlay.Adjacent(location, FacingType.East);
- Overlay southWall = Overlay.Adjacent(location, FacingType.South);
- Overlay westWall = Overlay.Adjacent(location, FacingType.West);
+ Overlay northWall = this.Overlay.Adjacent(location, FacingType.North);
+ Overlay eastWall = this.Overlay.Adjacent(location, FacingType.East);
+ Overlay southWall = this.Overlay.Adjacent(location, FacingType.South);
+ Overlay westWall = this.Overlay.Adjacent(location, FacingType.West);
int icon = 0;
if (northWall?.Type == overlay.Type)
{
@@ -665,7 +714,7 @@ namespace MobiusEditor.Model
private void UpdateConcreteOverlays(ISet locations)
{
- foreach (var (cell, overlay) in Overlay.IntersectsWithCells(locations).Where(o => o.Value.Type.IsConcrete))
+ foreach (var (cell, overlay) in this.Overlay.IntersectsWithCells(locations).Where(o => o.Value.Type.IsConcrete))
{
// in order: top, topnext, next, bottomnext, bottom
FacingType[] even = { FacingType.North, FacingType.NorthWest, FacingType.West, FacingType.SouthWest, FacingType.South };
@@ -675,7 +724,7 @@ namespace MobiusEditor.Model
Boolean[] conc = new bool[cells.Length];
for (int i = 0; i < cells.Length; i++)
{
- var neighbor = Overlay.Adjacent(cell, cells[i]);
+ var neighbor = this.Overlay.Adjacent(cell, cells[i]);
if (neighbor != null && neighbor.Type == overlay.Type)
{
int ic = overlay.Icon;
@@ -732,7 +781,7 @@ namespace MobiusEditor.Model
private void UpdateConcreteOverlays_ORIG(ISet locations)
{
- foreach (var (cell, overlay) in Overlay.IntersectsWithCells(locations).Where(o => o.Value.Type.IsConcrete))
+ foreach (var (cell, overlay) in this.Overlay.IntersectsWithCells(locations).Where(o => o.Value.Type.IsConcrete))
{
// Original logic as it is in the game code. Still doesn't match reality, probably due to bugs in the logic to add side cells.
FacingType[] odd = { FacingType.North, FacingType.NorthEast, FacingType.East, FacingType.SouthEast, FacingType.South };
@@ -742,7 +791,7 @@ namespace MobiusEditor.Model
int index = 0;
for (int i = 0; i < cells.Length; i++)
{
- var neighbor = Overlay.Adjacent(cell, cells[i]);
+ var neighbor = this.Overlay.Adjacent(cell, cells[i]);
if (neighbor != null && neighbor.Type == overlay.Type)
{
int ic = overlay.Icon;
@@ -853,7 +902,7 @@ namespace MobiusEditor.Model
Dictionary replaceIcons = new Dictionary();
int fillIcon;
TemplateType fillTile;
- SplitTileInfo(fillType, out fillTile, out fillIcon, "fillType", false);
+ this.SplitTileInfo(fillType, out fillTile, out fillIcon, "fillType", false);
if (fillTile != null)
{
Point? fillPoint = fillTile.GetIconPoint(fillIcon);
@@ -867,7 +916,7 @@ namespace MobiusEditor.Model
string tileType = kvp.Value;
int tileIcon;
TemplateType tile;
- SplitTileInfo(tileType, out tile, out tileIcon, "types", false);
+ this.SplitTileInfo(tileType, out tile, out tileIcon, "types", false);
replaceTypes[kvp.Key] = tile;
if (tile != null)
{
@@ -945,7 +994,7 @@ namespace MobiusEditor.Model
{
if (!safe)
{
- throw new ArgumentException(String.Format("Tile type '{0}' does not exist in theater {1}.", tileType, Theater.Name), context);
+ throw new ArgumentException(String.Format("Tile type '{0}' does not exist in theater {1}.", tileType, this.Theater.Name), context);
}
else
{
@@ -956,26 +1005,26 @@ namespace MobiusEditor.Model
public string GetCellDescription(Point location, Point subPixel)
{
- if (!Metrics.GetCell(location, out int cell))
+ if (!this.Metrics.GetCell(location, out int cell))
{
return "No cell";
}
- bool inBounds = Bounds.Contains(location);
+ bool inBounds = this.Bounds.Contains(location);
var sb = new StringBuilder();
sb.AppendFormat("X = {0}, Y = {1}, Cell = {2}", location.X, location.Y, cell);
- var template = Templates[cell];
+ var template = this.Templates[cell];
var templateType = template?.Type;
if (templateType != null)
{
sb.AppendFormat(", Template = {0} ({1})", templateType.DisplayName, template.Icon);
}
- var smudge = Smudge[cell];
+ var smudge = this.Smudge[cell];
var smudgeType = smudge?.Type;
if (smudgeType != null)
{
sb.AppendFormat(", Smudge = {0}{1}", smudgeType.DisplayName, smudgeType.IsAutoBib ? " (Attached)" : String.Empty);
}
- var overlay = Overlay[cell];
+ var overlay = this.Overlay[cell];
var overlayType = overlay?.Type;
if (overlayType != null)
{
@@ -988,13 +1037,13 @@ namespace MobiusEditor.Model
sb.AppendFormat(", Overlay = {0}", overlayType.DisplayName);
}
}
- var terrain = Technos[location] as Terrain;
+ var terrain = this.Technos[location] as Terrain;
var terrainType = terrain?.Type;
if (terrainType != null)
{
sb.AppendFormat(", Terrain = {0}", terrainType.DisplayName);
}
- if (Technos[location] is InfantryGroup infantryGroup)
+ if (this.Technos[location] is InfantryGroup infantryGroup)
{
InfantryStoppingType i = InfantryGroup.ClosestStoppingTypes(subPixel).First();
Infantry inf = infantryGroup.Infantry[(int)i];
@@ -1003,13 +1052,13 @@ namespace MobiusEditor.Model
sb.AppendFormat(", Infantry = {0} ({1})", inf.Type.DisplayName, InfantryGroup.GetStoppingTypeName(i));
}
}
- var unit = Technos[location] as Unit;
+ var unit = this.Technos[location] as Unit;
var unitType = unit?.Type;
if (unitType != null)
{
sb.AppendFormat(", Unit = {0}", unitType.DisplayName);
}
- var building = Buildings[location] as Building;
+ var building = this.Buildings[location] as Building;
var buildingType = building?.Type;
if (buildingType != null)
{
@@ -1020,39 +1069,39 @@ namespace MobiusEditor.Model
public HouseType GetBaseHouse(GameType gameType)
{
- House noneHouse = HousesIncludingNone.Where(h => h.Type.ID < 0).FirstOrDefault();
+ House noneHouse = this.HousesIncludingNone.Where(h => h.Type.ID < 0).FirstOrDefault();
if (noneHouse != null && gameType == GameType.TiberianDawn)
{
return noneHouse.Type;
}
else
{
- return HouseTypes.Where(h => h.Equals(BasicSection.BasePlayer)).FirstOrDefault() ?? HouseTypes.First();
+ return this.HouseTypes.Where(h => h.Equals(this.BasicSection.BasePlayer)).FirstOrDefault() ?? this.HouseTypes.First();
}
}
private void RemoveBibs(Building building)
{
- var bibCells = Smudge.IntersectsWithCells(building.BibCells).Where(x => x.Value.Type.IsAutoBib).Select(x => x.Cell).ToArray();
+ var bibCells = this.Smudge.IntersectsWithCells(building.BibCells).Where(x => x.Value.Type.IsAutoBib).Select(x => x.Cell).ToArray();
foreach (var cell in bibCells)
{
- Smudge[cell] = null;
+ this.Smudge[cell] = null;
}
building.BibCells.Clear();
}
private void AddBibs(Point location, Building building)
{
- Dictionary bibPoints = building.GetBib(location, SmudgeTypes);
+ Dictionary bibPoints = building.GetBib(location, this.SmudgeTypes);
if (bibPoints == null)
{
return;
}
foreach(Point p in bibPoints.Keys)
{
- if (Metrics.GetCell(p, out int subCell))
+ if (this.Metrics.GetCell(p, out int subCell))
{
- Smudge[subCell] = bibPoints[p];
+ this.Smudge[subCell] = bibPoints[p];
building.BibCells.Add(subCell);
}
}
@@ -1060,37 +1109,37 @@ namespace MobiusEditor.Model
public Map Clone(bool forPreview)
{
- Waypoint[] wpPreview = new Waypoint[Waypoints.Length + (forPreview ? 1 : 0)];
- Array.Copy(Waypoints, wpPreview, Waypoints.Length);
+ Waypoint[] wpPreview = new Waypoint[this.Waypoints.Length + (forPreview ? 1 : 0)];
+ Array.Copy(this.Waypoints, wpPreview, this.Waypoints.Length);
if (forPreview)
{
- wpPreview[Waypoints.Length] = new Waypoint("", null);
+ wpPreview[this.Waypoints.Length] = new Waypoint("", null);
}
// This is a shallow clone; the map is new, but the placed contents all still reference the original objects.
// These shallow copies are used for map preview during editing, where dummy objects can be added without any issue.
- var map = new Map(BasicSection, Theater, Metrics.Size, HouseType, HouseTypesIncludingNone,
- FlagColors, TheaterTypes, TemplateTypes, TerrainTypes, OverlayTypes, SmudgeTypes,
- EventTypes, CellEventTypes, UnitEventTypes, StructureEventTypes, TerrainEventTypes,
- ActionTypes, CellActionTypes, UnitActionTypes, StructureActionTypes, TerrainActionTypes,
- MissionTypes, inputMissionArmed, inputMissionUnarmed, inputMissionHarvest, inputMissionAircraft,
- UnitDirectionTypes, BuildingDirectionTypes, AllInfantryTypes, AllUnitTypes, BuildingTypes, TeamMissionTypes,
- AllTeamTechnoTypes, wpPreview, DropZoneRadius, GapRadius, RadarJamRadius, MovieTypes, MovieEmpty, ThemeTypes, ThemeEmpty,
- TiberiumOrGoldValue, GemValue)
+ var map = new Map(this.BasicSection, this.Theater, this.Metrics.Size, this.HouseType, this.HouseTypesIncludingNone,
+ this.FlagColors, this.TheaterTypes, this.TemplateTypes, this.TerrainTypes, this.OverlayTypes, this.SmudgeTypes,
+ this.EventTypes, this.CellEventTypes, this.UnitEventTypes, this.BuildingEventTypes, this.TerrainEventTypes,
+ this.ActionTypes, this.CellActionTypes, this.UnitActionTypes, this.BuildingActionTypes, this.TerrainActionTypes,
+ this.MissionTypes, this.inputMissionArmed, this.inputMissionUnarmed, this.inputMissionHarvest, this.inputMissionAircraft,
+ this.UnitDirectionTypes, this.BuildingDirectionTypes, this.AllInfantryTypes, this.AllUnitTypes, this.BuildingTypes, this.TeamMissionTypes,
+ this.AllTeamTechnoTypes, wpPreview, this.DropZoneRadius, this.GapRadius, this.RadarJamRadius, this.MovieTypes, this.MovieEmpty, this.ThemeTypes, this.ThemeEmpty,
+ this.TiberiumOrGoldValue, this.GemValue)
{
TopLeft = TopLeft,
Size = Size
};
map.BeginUpdate();
- MapSection.CopyTo(map.MapSection);
- BriefingSection.CopyTo(map.BriefingSection);
- SteamSection.CopyTo(map.SteamSection);
- Array.Copy(Houses, map.Houses, map.Houses.Length);
- map.Triggers.AddRange(Triggers);
- Templates.CopyTo(map.Templates);
- Overlay.CopyTo(map.Overlay);
- Smudge.CopyTo(map.Smudge);
- CellTriggers.CopyTo(map.CellTriggers);
- foreach (var (location, occupier) in Technos)
+ this.MapSection.CopyTo(map.MapSection);
+ this.BriefingSection.CopyTo(map.BriefingSection);
+ this.SteamSection.CopyTo(map.SteamSection);
+ Array.Copy(this.Houses, map.Houses, map.Houses.Length);
+ map.Triggers.AddRange(this.Triggers);
+ this.Templates.CopyTo(map.Templates);
+ this.Overlay.CopyTo(map.Overlay);
+ this.Smudge.CopyTo(map.Smudge);
+ this.CellTriggers.CopyTo(map.CellTriggers);
+ foreach (var (location, occupier) in this.Technos)
{
if (occupier is InfantryGroup infantryGroup)
{
@@ -1105,24 +1154,24 @@ namespace MobiusEditor.Model
map.Technos.Add(location, occupier);
}
}
- foreach (var (location, building) in Buildings)
+ foreach (var (location, building) in this.Buildings)
{
// Silly side effect: this fixes any building bibs.
map.Buildings.Add(location, building);
}
- map.TeamTypes.AddRange(TeamTypes);
+ map.TeamTypes.AddRange(this.TeamTypes);
map.EndUpdate();
return map;
}
public IEnumerable FilterCellTriggers()
{
- return FilterCellTriggers(this.Triggers);
+ return this.FilterCellTriggers(this.Triggers);
}
public IEnumerable FilterCellTriggers(IEnumerable triggers)
{
- foreach (Trigger trigger in FilterTriggersByEvent(CellEventTypes, triggers).Concat(FilterTriggersByAction(CellActionTypes, triggers).Distinct()))
+ foreach (Trigger trigger in FilterTriggersByEvent(this.CellEventTypes, triggers).Concat(this.FilterTriggersByAction(this.CellActionTypes, triggers).Distinct()))
{
yield return trigger;
}
@@ -1130,12 +1179,12 @@ namespace MobiusEditor.Model
public IEnumerable FilterUnitTriggers()
{
- return FilterUnitTriggers(this.Triggers);
+ return this.FilterUnitTriggers(this.Triggers);
}
public IEnumerable FilterUnitTriggers(IEnumerable triggers)
{
- foreach (Trigger trigger in FilterTriggersByEvent(UnitEventTypes, triggers).Concat(FilterTriggersByAction(UnitActionTypes, triggers).Distinct()))
+ foreach (Trigger trigger in FilterTriggersByEvent(this.UnitEventTypes, triggers).Concat(this.FilterTriggersByAction(this.UnitActionTypes, triggers).Distinct()))
{
yield return trigger;
}
@@ -1143,12 +1192,12 @@ namespace MobiusEditor.Model
public IEnumerable FilterStructureTriggers()
{
- return FilterStructureTriggers(this.Triggers);
+ return this.FilterStructureTriggers(this.Triggers);
}
public IEnumerable FilterStructureTriggers(IEnumerable triggers)
{
- foreach (Trigger trigger in FilterTriggersByEvent(StructureEventTypes, triggers).Concat(FilterTriggersByAction(StructureActionTypes, triggers).Distinct()))
+ foreach (Trigger trigger in FilterTriggersByEvent(this.BuildingEventTypes, triggers).Concat(this.FilterTriggersByAction(this.BuildingActionTypes, triggers).Distinct()))
{
yield return trigger;
}
@@ -1156,12 +1205,12 @@ namespace MobiusEditor.Model
public IEnumerable FilterTerrainTriggers()
{
- return FilterTerrainTriggers(this.Triggers);
+ return this.FilterTerrainTriggers(this.Triggers);
}
public IEnumerable FilterTerrainTriggers(IEnumerable triggers)
{
- foreach (Trigger trigger in FilterTriggersByEvent(TerrainEventTypes, triggers).Concat(FilterTriggersByAction(TerrainActionTypes, triggers).Distinct()))
+ foreach (Trigger trigger in FilterTriggersByEvent(this.TerrainEventTypes, triggers).Concat(this.FilterTriggersByAction(this.TerrainActionTypes, triggers).Distinct()))
{
yield return trigger;
}
@@ -1214,7 +1263,7 @@ namespace MobiusEditor.Model
public IEnumerable GetAllTechnos()
{
- foreach (var (location, occupier) in Technos)
+ foreach (var (location, occupier) in this.Technos)
{
if (occupier is InfantryGroup infantryGroup)
{
@@ -1235,28 +1284,28 @@ namespace MobiusEditor.Model
public string GetDefaultMission(ITechnoType techno)
{
- return GetDefaultMission(techno, DefaultMissionArmed);
+ return this.GetDefaultMission(techno, this.DefaultMissionArmed);
}
public string GetDefaultMission(ITechnoType techno, String currentMission)
{
if (techno.IsHarvester)
{
- return DefaultMissionHarvest;
+ return this.DefaultMissionHarvest;
}
if (techno.IsAircraft && !techno.IsFixedWing)
{
// Ground-landable aircraft. Default order should be 'Unload' to make it land on the spot it spawned on.
- return DefaultMissionAircraft;
+ return this.DefaultMissionAircraft;
}
if (!techno.IsArmed)
{
- return DefaultMissionUnarmed;
+ return this.DefaultMissionUnarmed;
}
// Automatically switch from other default missions to the general 'Guard' one, but don't change custom-picked mission like 'Hunt4.
- if (currentMission == DefaultMissionHarvest || currentMission == DefaultMissionAircraft || currentMission == DefaultMissionUnarmed)
+ if (currentMission == this.DefaultMissionHarvest || currentMission == this.DefaultMissionAircraft || currentMission == this.DefaultMissionUnarmed)
{
- return DefaultMissionArmed;
+ return this.DefaultMissionArmed;
}
return currentMission;
}
@@ -1264,7 +1313,7 @@ namespace MobiusEditor.Model
public ICellOccupier FindBlockingObject(int cell, ICellOccupier obj, out int blockingCell)
{
blockingCell = -1;
- if (Metrics.GetLocation(cell, out Point p))
+ if (this.Metrics.GetLocation(cell, out Point p))
{
bool[,] mask;
int ylen;
@@ -1281,14 +1330,14 @@ namespace MobiusEditor.Model
{
if (mask[y, x])
{
- if (!Metrics.GetCell(new Point(p.X + x, p.Y + y), out int targetCell))
+ if (!this.Metrics.GetCell(new Point(p.X + x, p.Y + y), out int targetCell))
{
blockingCell = -1;
return null;
}
- ICellOccupier techno = Technos[targetCell];
- ICellOccupier b = Buildings[targetCell];
+ ICellOccupier techno = this.Technos[targetCell];
+ ICellOccupier b = this.Buildings[targetCell];
if (techno != null || b != null)
{
blockingCell = targetCell;
@@ -1307,13 +1356,13 @@ namespace MobiusEditor.Model
{
if (mask[y, x])
{
- if (!Metrics.GetCell(new Point(p.X + x, p.Y + y), out int targetCell))
+ if (!this.Metrics.GetCell(new Point(p.X + x, p.Y + y), out int targetCell))
{
blockingCell = -1;
return null;
}
- ICellOccupier techno = Technos[targetCell];
- ICellOccupier b = Buildings[targetCell];
+ ICellOccupier techno = this.Technos[targetCell];
+ ICellOccupier b = this.Buildings[targetCell];
if (techno != null || b != null)
{
blockingCell = targetCell;
@@ -1329,28 +1378,28 @@ namespace MobiusEditor.Model
public TGA GeneratePreview(Size previewSize, GameType gameType, bool renderAll, bool sharpen)
{
MapLayerFlag toRender = MapLayerFlag.Template | (renderAll ? MapLayerFlag.OverlayAll | MapLayerFlag.Smudge | MapLayerFlag.Technos : MapLayerFlag.Resources);
- return GeneratePreview(previewSize, gameType, toRender, true, true, sharpen);
+ return this.GeneratePreview(previewSize, gameType, toRender, true, true, sharpen);
}
public TGA GeneratePreview(Size previewSize, GameType gameType, MapLayerFlag toRender, bool smooth, bool crop, bool sharpen)
{
Rectangle mapBounds;
- HashSet locations = Metrics.Bounds.Points().ToHashSet();;
+ HashSet locations = this.Metrics.Bounds.Points().ToHashSet();;
if (crop)
{
- mapBounds = new Rectangle(Bounds.Left * Globals.OriginalTileWidth, Bounds.Top * Globals.OriginalTileHeight,
- Bounds.Width * Globals.OriginalTileWidth, Bounds.Height * Globals.OriginalTileHeight);
+ mapBounds = new Rectangle(this.Bounds.Left * Globals.OriginalTileWidth, this.Bounds.Top * Globals.OriginalTileHeight,
+ this.Bounds.Width * Globals.OriginalTileWidth, this.Bounds.Height * Globals.OriginalTileHeight);
//locations = Bounds.Points().ToHashSet();
}
else
{
- mapBounds = new Rectangle(0, 0, Metrics.Width * Globals.OriginalTileWidth, Metrics.Height * Globals.OriginalTileHeight);
+ mapBounds = new Rectangle(0, 0, this.Metrics.Width * Globals.OriginalTileWidth, this.Metrics.Height * Globals.OriginalTileHeight);
//locations
}
var previewScale = Math.Min(previewSize.Width / (float)mapBounds.Width, previewSize.Height / (float)mapBounds.Height);
var scaledSize = new Size((int)(previewSize.Width / previewScale), (int)(previewSize.Height / previewScale));
- using (var fullBitmap = new Bitmap(Metrics.Width * Globals.OriginalTileWidth, Metrics.Height * Globals.OriginalTileHeight))
+ using (var fullBitmap = new Bitmap(this.Metrics.Width * Globals.OriginalTileWidth, this.Metrics.Height * Globals.OriginalTileHeight))
using (var croppedBitmap = new Bitmap(previewSize.Width, previewSize.Height))
{
using (var g = Graphics.FromImage(fullBitmap))
@@ -1384,30 +1433,30 @@ namespace MobiusEditor.Model
public TGA GenerateMapPreview(GameType gameType, bool renderAll)
{
- return GeneratePreview(Globals.MapPreviewSize, gameType, renderAll, false);
+ return this.GeneratePreview(Globals.MapPreviewSize, gameType, renderAll, false);
}
public TGA GenerateWorkshopPreview(GameType gameType, bool renderAll)
{
- return GeneratePreview(Globals.WorkshopPreviewSize, gameType, renderAll, true);
+ return this.GeneratePreview(Globals.WorkshopPreviewSize, gameType, renderAll, true);
}
object ICloneable.Clone()
{
- return Clone(false);
+ return this.Clone(false);
}
private void Overlay_CellChanged(object sender, CellChangedEventArgs e)
{
if (e.OldValue?.Type.IsWall ?? false)
{
- Buildings.Remove(e.OldValue);
+ this.Buildings.Remove(e.OldValue);
}
if (e.Value?.Type.IsWall ?? false)
{
- Buildings.Add(e.Location, e.Value);
+ this.Buildings.Add(e.Location, e.Value);
}
- if (updating)
+ if (this.updating)
{
return;
}
@@ -1434,16 +1483,16 @@ namespace MobiusEditor.Model
{
continue;
}
- if (!invalidateLayers.TryGetValue(layer, out ISet locations))
+ if (!this.invalidateLayers.TryGetValue(layer, out ISet locations))
{
locations = new HashSet();
- invalidateLayers[layer] = locations;
+ this.invalidateLayers[layer] = locations;
}
locations.UnionWith(Rectangle.Inflate(new Rectangle(e.Location, new Size(1, 1)), 1, 1).Points());
}
- if (updateCount == 0)
+ if (this.updateCount == 0)
{
- Update();
+ this.Update();
}
}
@@ -1451,13 +1500,13 @@ namespace MobiusEditor.Model
{
if (e.Occupier is ICellOverlapper overlapper)
{
- if (updateCount == 0)
+ if (this.updateCount == 0)
{
- Overlappers.Add(e.Location, overlapper);
+ this.Overlappers.Add(e.Location, overlapper);
}
else
{
- invalidateOverlappers = true;
+ this.invalidateOverlappers = true;
}
}
}
@@ -1466,13 +1515,13 @@ namespace MobiusEditor.Model
{
if (e.Occupier is ICellOverlapper overlapper)
{
- if (updateCount == 0)
+ if (this.updateCount == 0)
{
- Overlappers.Remove(overlapper);
+ this.Overlappers.Remove(overlapper);
}
else
{
- invalidateOverlappers = true;
+ this.invalidateOverlappers = true;
}
}
}
@@ -1481,12 +1530,12 @@ namespace MobiusEditor.Model
{
if (e.Occupier is Building building)
{
- Technos.Add(e.Location, e.Occupier, building.Type.BaseOccupyMask);
- AddBibs(e.Location, building);
+ this.Technos.Add(e.Location, e.Occupier, building.Type.BaseOccupyMask);
+ this.AddBibs(e.Location, building);
}
else
{
- Technos.Add(e.Location, e.Occupier);
+ this.Technos.Add(e.Location, e.Occupier);
}
}
@@ -1494,23 +1543,23 @@ namespace MobiusEditor.Model
{
if (e.Occupier is Building building)
{
- RemoveBibs(building);
+ this.RemoveBibs(building);
}
- Technos.Remove(e.Occupier);
+ this.Technos.Remove(e.Occupier);
}
public bool RemoveExpansionUnits()
{
HashSet refreshPoints = new HashSet();
bool changed = false;
- if (BasicSection.ExpansionEnabled)
+ if (this.BasicSection.ExpansionEnabled)
{
// Expansion is enabled. Nothing to do.
return false;
}
// Technos on map
List<(Point, ICellOccupier)> toDelete = new List<(Point, ICellOccupier)>();
- foreach ((Point p, ICellOccupier occup) in Technos)
+ foreach ((Point p, ICellOccupier occup) in this.Technos)
{
if (occup is Unit un)
{
@@ -1531,13 +1580,13 @@ namespace MobiusEditor.Model
{
if (occup is Unit un)
{
- Rectangle? refreshArea = Overlappers[un];
+ Rectangle? refreshArea = this.Overlappers[un];
if (refreshArea.HasValue)
{
refreshPoints.UnionWith(refreshArea.Value.Points());
}
//mapPanel.Invalidate(map, un);
- Technos.Remove(occup);
+ this.Technos.Remove(occup);
changed = true;
}
else if (occup is InfantryGroup infantryGroup)
@@ -1552,7 +1601,7 @@ namespace MobiusEditor.Model
}
}
bool delGroup = inf.All(i => i == null);
- Rectangle? refreshArea = Overlappers[infantryGroup];
+ Rectangle? refreshArea = this.Overlappers[infantryGroup];
if (refreshArea.HasValue)
{
refreshPoints.UnionWith(refreshArea.Value.Points());
@@ -1560,12 +1609,12 @@ namespace MobiusEditor.Model
//mapPanel.Invalidate(map, infantryGroup);
if (delGroup)
{
- Technos.Remove(infantryGroup);
+ this.Technos.Remove(infantryGroup);
}
}
}
// Teamtypes
- foreach (TeamType teamtype in TeamTypes)
+ foreach (TeamType teamtype in this.TeamTypes)
{
List toRemove = new List();
foreach (TeamTypeClass ttclass in teamtype.Classes)
@@ -1605,7 +1654,7 @@ namespace MobiusEditor.Model
{
continue;
}
- foreach ((Point location, Building building) in Buildings.OfType().Where(x => x.Occupier.IsPrebuilt))
+ foreach ((Point location, Building building) in this.Buildings.OfType().Where(x => x.Occupier.IsPrebuilt))
{
if (String.Equals(building.Trigger, name1, StringComparison.OrdinalIgnoreCase))
{
@@ -1615,7 +1664,7 @@ namespace MobiusEditor.Model
building.Trigger = name2;
}
}
- foreach (ITechno techno in GetAllTechnos())
+ foreach (ITechno techno in this.GetAllTechnos())
{
if (String.Equals(techno.Trigger, name1, StringComparison.OrdinalIgnoreCase))
{
@@ -1627,7 +1676,7 @@ namespace MobiusEditor.Model
techno.Trigger = name2;
}
}
- foreach (TeamType team in TeamTypes)
+ foreach (TeamType team in this.TeamTypes)
{
if (String.Equals(team.Trigger, name1, StringComparison.OrdinalIgnoreCase))
{
@@ -1639,7 +1688,7 @@ namespace MobiusEditor.Model
team.Trigger = name2;
}
}
- foreach ((int cell, CellTrigger value) in CellTriggers)
+ foreach ((int cell, CellTrigger value) in this.CellTriggers)
{
if (String.Equals(value.Trigger, name1, StringComparison.OrdinalIgnoreCase))
{
@@ -1664,30 +1713,30 @@ namespace MobiusEditor.Model
{
// Clean techno types
HashSet availableTriggers = triggers.Select(t => t.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
- HashSet availableUnitTriggers = FilterUnitTriggers(triggers).Select(t => t.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
- HashSet availableBuildingTriggers = FilterStructureTriggers(triggers).Select(t => t.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
- HashSet availableTerrainTriggers = FilterTerrainTriggers(triggers).Select(t => t.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
- foreach (ITechno techno in GetAllTechnos())
+ HashSet availableUnitTriggers = this.FilterUnitTriggers(triggers).Select(t => t.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
+ HashSet availableBuildingTriggers = this.FilterStructureTriggers(triggers).Select(t => t.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
+ HashSet availableTerrainTriggers = this.FilterTerrainTriggers(triggers).Select(t => t.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
+ foreach (ITechno techno in this.GetAllTechnos())
{
if (techno is Infantry infantry)
{
- CheckTechnoTrigger(infantry, availableUnitTriggers, undoList, redoList);
+ this.CheckTechnoTrigger(infantry, availableUnitTriggers, undoList, redoList);
}
else if (techno is Unit unit)
{
- CheckTechnoTrigger(unit, availableUnitTriggers, undoList, redoList);
+ this.CheckTechnoTrigger(unit, availableUnitTriggers, undoList, redoList);
}
else if (techno is Building building)
{
- CheckTechnoTrigger(building, availableBuildingTriggers, undoList, redoList);
+ this.CheckTechnoTrigger(building, availableBuildingTriggers, undoList, redoList);
}
else if (techno is Terrain terrain)
{
- CheckTechnoTrigger(terrain, availableTerrainTriggers, undoList, redoList);
+ this.CheckTechnoTrigger(terrain, availableTerrainTriggers, undoList, redoList);
}
}
// Clean teamtypes
- foreach (var team in TeamTypes)
+ foreach (var team in this.TeamTypes)
{
String trig = team.Trigger;
if (!Trigger.IsEmpty(trig) && !availableUnitTriggers.Contains(trig))
@@ -1719,7 +1768,7 @@ namespace MobiusEditor.Model
trig.Action2.Trigger = Trigger.None;
}
}
- CleanUpCellTriggers(triggers, undoList, redoList, cellTriggerLocations);
+ this.CleanUpCellTriggers(triggers, undoList, redoList, cellTriggerLocations);
}
private void CheckTechnoTrigger(ITechno techno, HashSet availableTriggers, Dictionary
protected override MapLayerFlag ManuallyHandledLayers => MapLayerFlag.None;
- private readonly Label totalResourcesLbl;
private readonly Label boundsResourcesLbl;
private readonly NumericUpDown brushSizeNud;
private readonly CheckBox gemsCheckBox;
@@ -51,11 +50,10 @@ namespace MobiusEditor.Tools
private readonly Dictionary undoOverlays = new Dictionary();
private readonly Dictionary redoOverlays = new Dictionary();
- 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 url)
: base(mapPanel, layers, statusLbl, plugin, url)
{
- this.totalResourcesLbl = totalResourcesLbl;
this.boundsResourcesLbl = boundsResourcesLbl;
this.brushSizeNud = brushSizeNud;
this.gemsCheckBox = gemsCheckBox;
@@ -315,11 +313,7 @@ namespace MobiusEditor.Tools
private void Update()
{
- if (totalResourcesLbl != null)
- {
- totalResourcesLbl.Text = map.TotalResources.ToString();
- }
- boundsResourcesLbl.Text = map.ResourcesInBounds.ToString();
+ boundsResourcesLbl.Text = map.ResourcesOnMap.ToString();
if (map.OverlayTypes.Any(t => t.IsGem))
{
gemsCheckBox.Visible = true;
diff --git a/CnCTDRAMapEditor/Utility/ImageUtils.cs b/CnCTDRAMapEditor/Utility/ImageUtils.cs
index 19eb438..b9a66e5 100644
--- a/CnCTDRAMapEditor/Utility/ImageUtils.cs
+++ b/CnCTDRAMapEditor/Utility/ImageUtils.cs
@@ -379,11 +379,36 @@ namespace MobiusEditor.Utility
BitmapData data = null;
try
{
- data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
- var bpp = Image.GetPixelFormatSize(data.PixelFormat) / 8;
+ PixelFormat dataFormat = bitmap.PixelFormat;
+ if (dataFormat == PixelFormat.Format24bppRgb || dataFormat == PixelFormat.Format32bppRgb)
+ {
+ return new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ }
+ List transColors = null;
+ if ((dataFormat & PixelFormat.Indexed) == PixelFormat.Indexed)
+ {
+ dataFormat = PixelFormat.Format8bppIndexed;
+ Color[] entries = bitmap.Palette.Entries;
+ transColors = new List();
+ 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];
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
{
@@ -394,8 +419,22 @@ namespace MobiusEditor.Utility
}
}
- public static Rectangle CalculateOpaqueBounds(byte[] data, int width, int height, int bytespp, int stride)
+ ///
+ /// Calculates the actually opaque bounds of a 24bpp or 32bpp image given as bytes.
+ ///
+ /// Image data.
+ /// Image width.
+ /// Image height.
+ /// Bytes per pixel.
+ /// Stride of the image.
+ ///
+ 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.
int lineWidth = width * bytespp;
bool isTransparentRow(int y)
@@ -462,5 +501,77 @@ namespace MobiusEditor.Utility
return opaqueBounds;
}
+ private static Rectangle CalculateOpaqueBounds8bpp(byte[] data, int width, int height, int stride, List transparentColors)
+ {
+ HashSet trMap = new HashSet(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;
+ }
+
}
}
diff --git a/CnCTDRAMapEditor/Utility/TeamColor.cs b/CnCTDRAMapEditor/Utility/TeamColor.cs
index 6ee6600..0f5c6d2 100644
--- a/CnCTDRAMapEditor/Utility/TeamColor.cs
+++ b/CnCTDRAMapEditor/Utility/TeamColor.cs
@@ -30,12 +30,6 @@ namespace MobiusEditor.Utility
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;
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.Variant = col.Variant;
- this.classicRemap = col.ClassicRemap;
- this.classicRemapStructures = col.ClassicRemapStructures;
this.lowerBounds = col.LowerBounds;
this.upperBounds = col.UpperBounds;
this.fudge = col.Fudge;
@@ -128,14 +120,6 @@ namespace MobiusEditor.Utility
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)
{
XmlDocument xmlDoc = new XmlDocument();
@@ -262,17 +246,27 @@ namespace MobiusEditor.Utility
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;
try
{
data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, image.PixelFormat);
- var bytesPerPixel = Image.GetPixelFormatSize(data.PixelFormat) / 8;
- var bytes = new byte[data.Stride * data.Height];
+ byte[] bytes = new byte[data.Stride * data.Height];
Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
int width = data.Width;
int height = data.Height;
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);
Marshal.Copy(bytes, 0, data.Scan0, bytes.Length);
}
@@ -287,26 +281,32 @@ namespace MobiusEditor.Utility
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);
float frac(float x) => x - (int)x;
float lerp(float x, float y, float t) => (x * (1.0f - t)) + (y * t);
float saturate(float x) => Math.Max(0.0f, Math.Min(1.0f, x));
- // Precalculate some stuff.
- var lowerHue = this.LowerBounds.GetHue() / 360.0f;
- var upperHue = this.UpperBounds.GetHue() / 360.0f;
- var lowerHueFudge = lowerHue - this.Fudge;
- var upperHueFudge = upperHue + this.Fudge;
- var hueError = (upperHueFudge - lowerHueFudge) / (upperHue - lowerHue);
- var hueShift = this.HSVShift.X;
- var satShift = this.HSVShift.Y;
- var valShift = this.HSVShift.Z;
- // Optimisation: since we got the opaque bounds calculated anyway, might as well use them and only process what's inside.
- int lineStart = bounds.Top * stride;
+ // Precalculate some stuff.
+ var lowerHue = this.LowerBounds.GetHue() / 360.0f;
+ var upperHue = this.UpperBounds.GetHue() / 360.0f;
+ var lowerHueFudge = lowerHue - this.Fudge;
+ var upperHueFudge = upperHue + this.Fudge;
+ var hueError = (upperHueFudge - lowerHueFudge) / (upperHue - lowerHue);
+ var hueShift = this.HSVShift.X;
+ var satShift = this.HSVShift.Y;
+ var valShift = this.HSVShift.Z;
+ // Optimisation: since we got the opaque bounds calculated anyway, might as well use them and only process what's inside.
+ int lineStart = bounds.Top * stride;
for (int y = bounds.Top; y < bounds.Bottom; y++)
{
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]);
(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);
diff --git a/MANUAL.md b/MANUAL.md
index 96e1dd6..a6054bc 100644
--- a/MANUAL.md
+++ b/MANUAL.md
@@ -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.
-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:
diff --git a/README.md b/README.md
index 8ba0093..2234937 100644
--- a/README.md
+++ b/README.md
@@ -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.
-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 `` under the "ModsToLoadRA" setting with `2978875641;GraphicsFixesRA`**