mirror of
https://github.com/electronicarts/CnC_Remastered_Collection.git
synced 2026-04-28 03:36:58 +02:00
C&C Remastered Map Editor
Initial commit of C&C Remastered Map Editor code
This commit is contained in:
@@ -0,0 +1,681 @@
|
||||
//
|
||||
// Copyright 2020 Electronic Arts Inc.
|
||||
//
|
||||
// The Command & Conquer Map Editor and corresponding source code is free
|
||||
// software: you can redistribute it and/or modify it under the terms of
|
||||
// the GNU General Public License as published by the Free Software Foundation,
|
||||
// either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
// The Command & Conquer Map Editor and corresponding source code is distributed
|
||||
// in the hope that it will be useful, but with permitted additional restrictions
|
||||
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
|
||||
// distributed with this program. You should have received a copy of the
|
||||
// GNU General Public License along with permitted additional restrictions
|
||||
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
|
||||
using MobiusEditor.Interface;
|
||||
using MobiusEditor.Render;
|
||||
using MobiusEditor.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
using TGASharpLib;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
[Flags]
|
||||
public enum MapLayerFlag
|
||||
{
|
||||
None = 0,
|
||||
Basic = 1 << 0,
|
||||
Map = 1 << 1,
|
||||
Template = 1 << 2,
|
||||
Terrain = 1 << 3,
|
||||
Resources = 1 << 4,
|
||||
Walls = 1 << 5,
|
||||
Overlay = 1 << 6,
|
||||
Smudge = 1 << 7,
|
||||
Waypoints = 1 << 8,
|
||||
CellTriggers = 1 << 9,
|
||||
Houses = 1 << 10,
|
||||
Infantry = 1 << 11,
|
||||
Units = 1 << 12,
|
||||
Buildings = 1 << 13,
|
||||
Boundaries = 1 << 14,
|
||||
TechnoTriggers = 1 << 15,
|
||||
|
||||
OverlayAll = Resources | Walls | Overlay,
|
||||
Technos = Terrain | Walls | Infantry | Units | Buildings,
|
||||
|
||||
All = int.MaxValue
|
||||
}
|
||||
|
||||
public class MapContext : ITypeDescriptorContext
|
||||
{
|
||||
public IContainer Container { get; private set; }
|
||||
|
||||
public object Instance { get; private set; }
|
||||
|
||||
public PropertyDescriptor PropertyDescriptor { get; private set; }
|
||||
|
||||
public Map Map => Instance as Map;
|
||||
|
||||
public readonly bool FractionalPercentages;
|
||||
|
||||
public MapContext(Map map, bool fractionalPercentages)
|
||||
{
|
||||
Instance = map;
|
||||
FractionalPercentages = fractionalPercentages;
|
||||
}
|
||||
|
||||
public object GetService(Type serviceType) => null;
|
||||
|
||||
public void OnComponentChanged() { }
|
||||
|
||||
public bool OnComponentChanging() => true;
|
||||
}
|
||||
|
||||
public class Map : ICloneable
|
||||
{
|
||||
private int updateCount = 0;
|
||||
private bool updating = false;
|
||||
private IDictionary<MapLayerFlag, ISet<Point>> invalidateLayers = new Dictionary<MapLayerFlag, ISet<Point>>();
|
||||
private bool invalidateOverlappers;
|
||||
|
||||
public readonly BasicSection BasicSection;
|
||||
|
||||
public readonly MapSection MapSection = new MapSection();
|
||||
|
||||
public readonly BriefingSection BriefingSection = new BriefingSection();
|
||||
|
||||
public readonly SteamSection SteamSection = new SteamSection();
|
||||
|
||||
public TheaterType Theater { get => MapSection.Theater; set => MapSection.Theater = value; }
|
||||
|
||||
public Point TopLeft
|
||||
{
|
||||
get => new Point(MapSection.X, MapSection.Y);
|
||||
set { MapSection.X = value.X; MapSection.Y = value.Y; }
|
||||
}
|
||||
|
||||
public Size Size
|
||||
{
|
||||
get => new Size(MapSection.Width, MapSection.Height);
|
||||
set { MapSection.Width = value.Width; 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; }
|
||||
}
|
||||
|
||||
public readonly Type HouseType;
|
||||
|
||||
public readonly HouseType[] HouseTypes;
|
||||
|
||||
public readonly List<TheaterType> TheaterTypes;
|
||||
|
||||
public readonly List<TemplateType> TemplateTypes;
|
||||
|
||||
public readonly List<TerrainType> TerrainTypes;
|
||||
|
||||
public readonly List<OverlayType> OverlayTypes;
|
||||
|
||||
public readonly List<SmudgeType> SmudgeTypes;
|
||||
|
||||
public readonly string[] EventTypes;
|
||||
|
||||
public readonly string[] ActionTypes;
|
||||
|
||||
public readonly string[] MissionTypes;
|
||||
|
||||
public readonly List<DirectionType> DirectionTypes;
|
||||
|
||||
public readonly List<InfantryType> InfantryTypes;
|
||||
|
||||
public readonly List<UnitType> UnitTypes;
|
||||
|
||||
public readonly List<BuildingType> BuildingTypes;
|
||||
|
||||
public readonly string[] TeamMissionTypes;
|
||||
|
||||
public readonly CellMetrics Metrics;
|
||||
|
||||
public readonly CellGrid<Template> Templates;
|
||||
|
||||
public readonly CellGrid<Overlay> Overlay;
|
||||
|
||||
public readonly CellGrid<Smudge> Smudge;
|
||||
|
||||
public readonly OccupierSet<ICellOccupier> Technos;
|
||||
|
||||
public readonly OccupierSet<ICellOccupier> Buildings;
|
||||
|
||||
public readonly OverlapperSet<ICellOverlapper> Overlappers;
|
||||
|
||||
public readonly Waypoint[] Waypoints;
|
||||
|
||||
public readonly CellGrid<CellTrigger> CellTriggers;
|
||||
|
||||
public readonly ObservableCollection<Trigger> Triggers;
|
||||
|
||||
public readonly List<TeamType> TeamTypes;
|
||||
|
||||
public House[] Houses;
|
||||
|
||||
public readonly List<string> MovieTypes;
|
||||
|
||||
public int TiberiumOrGoldValue { get; set; }
|
||||
|
||||
public int GemValue { get; set; }
|
||||
|
||||
public int TotalResources
|
||||
{
|
||||
get
|
||||
{
|
||||
int totalResources = 0;
|
||||
foreach (var (cell, value) in Overlay)
|
||||
{
|
||||
if (value.Type.IsResource)
|
||||
{
|
||||
totalResources += (value.Icon + 1) * (value.Type.IsGem ? GemValue : TiberiumOrGoldValue);
|
||||
}
|
||||
}
|
||||
return totalResources;
|
||||
}
|
||||
}
|
||||
|
||||
public Map(BasicSection basicSection, TheaterType theater, Size cellSize, Type houseType,
|
||||
IEnumerable<HouseType> houseTypes, IEnumerable<TheaterType> theaterTypes, IEnumerable<TemplateType> templateTypes,
|
||||
IEnumerable<TerrainType> terrainTypes, IEnumerable<OverlayType> overlayTypes, IEnumerable<SmudgeType> smudgeTypes,
|
||||
IEnumerable<string> eventTypes, IEnumerable<string> actionTypes, IEnumerable<string> missionTypes,
|
||||
IEnumerable<DirectionType> directionTypes, IEnumerable<InfantryType> infantryTypes, IEnumerable<UnitType> unitTypes,
|
||||
IEnumerable<BuildingType> buildingTypes, IEnumerable<string> teamMissionTypes, IEnumerable<Waypoint> waypoints,
|
||||
IEnumerable<string> movieTypes)
|
||||
{
|
||||
BasicSection = basicSection;
|
||||
|
||||
HouseType = houseType;
|
||||
HouseTypes = houseTypes.ToArray();
|
||||
TheaterTypes = new List<TheaterType>(theaterTypes);
|
||||
TemplateTypes = new List<TemplateType>(templateTypes);
|
||||
TerrainTypes = new List<TerrainType>(terrainTypes);
|
||||
OverlayTypes = new List<OverlayType>(overlayTypes);
|
||||
SmudgeTypes = new List<SmudgeType>(smudgeTypes);
|
||||
EventTypes = eventTypes.ToArray();
|
||||
ActionTypes = actionTypes.ToArray();
|
||||
MissionTypes = missionTypes.ToArray();
|
||||
DirectionTypes = new List<DirectionType>(directionTypes);
|
||||
InfantryTypes = new List<InfantryType>(infantryTypes);
|
||||
UnitTypes = new List<UnitType>(unitTypes);
|
||||
BuildingTypes = new List<BuildingType>(buildingTypes);
|
||||
TeamMissionTypes = teamMissionTypes.ToArray();
|
||||
MovieTypes = new List<string>(movieTypes);
|
||||
|
||||
Metrics = new CellMetrics(cellSize);
|
||||
Templates = new CellGrid<Template>(Metrics);
|
||||
Overlay = new CellGrid<Overlay>(Metrics);
|
||||
Smudge = new CellGrid<Smudge>(Metrics);
|
||||
Technos = new OccupierSet<ICellOccupier>(Metrics);
|
||||
Buildings = new OccupierSet<ICellOccupier>(Metrics);
|
||||
Overlappers = new OverlapperSet<ICellOverlapper>(Metrics);
|
||||
Triggers = new ObservableCollection<Trigger>();
|
||||
TeamTypes = new List<TeamType>();
|
||||
Houses = HouseTypes.Select(t => { var h = (House)Activator.CreateInstance(HouseType, t); h.SetDefault(); return h; }).ToArray();
|
||||
Waypoints = waypoints.ToArray();
|
||||
CellTriggers = new CellGrid<CellTrigger>(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;
|
||||
Triggers.CollectionChanged += Triggers_CollectionChanged;
|
||||
}
|
||||
|
||||
public void BeginUpdate()
|
||||
{
|
||||
updateCount++;
|
||||
}
|
||||
|
||||
public void EndUpdate()
|
||||
{
|
||||
if (--updateCount == 0)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
public void InitTheater(GameType gameType)
|
||||
{
|
||||
foreach (var templateType in TemplateTypes)
|
||||
{
|
||||
templateType.Init(Theater);
|
||||
}
|
||||
|
||||
foreach (var smudgeType in SmudgeTypes)
|
||||
{
|
||||
smudgeType.Init(Theater);
|
||||
}
|
||||
|
||||
foreach (var overlayType in OverlayTypes)
|
||||
{
|
||||
overlayType.Init(Theater);
|
||||
}
|
||||
|
||||
foreach (var terrainType in TerrainTypes)
|
||||
{
|
||||
terrainType.Init(Theater);
|
||||
}
|
||||
|
||||
foreach (var infantryType in InfantryTypes)
|
||||
{
|
||||
infantryType.Init(gameType, Theater, HouseTypes.Where(h => h.Equals(infantryType.OwnerHouse)).FirstOrDefault(), DirectionTypes.Where(d => d.Facing == FacingType.South).First());
|
||||
}
|
||||
|
||||
foreach (var unitType in UnitTypes)
|
||||
{
|
||||
unitType.Init(gameType, Theater, HouseTypes.Where(h => h.Equals(unitType.OwnerHouse)).FirstOrDefault(), DirectionTypes.Where(d => d.Facing == FacingType.North).First());
|
||||
}
|
||||
|
||||
foreach (var buildingType in BuildingTypes)
|
||||
{
|
||||
buildingType.Init(gameType, Theater, HouseTypes.Where(h => h.Equals(buildingType.OwnerHouse)).FirstOrDefault(), DirectionTypes.Where(d => d.Facing == FacingType.North).First());
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
updating = true;
|
||||
|
||||
if (invalidateLayers.TryGetValue(MapLayerFlag.Resources, out ISet<Point> locations))
|
||||
{
|
||||
UpdateResourceOverlays(locations);
|
||||
}
|
||||
|
||||
if (invalidateLayers.TryGetValue(MapLayerFlag.Walls, out locations))
|
||||
{
|
||||
UpdateWallOverlays(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;
|
||||
}
|
||||
|
||||
private void UpdateResourceOverlays(ISet<Point> locations)
|
||||
{
|
||||
var tiberiumCounts = new int[] { 0, 1, 3, 4, 6, 7, 8, 10, 11 };
|
||||
var gemCounts = new int[] { 0, 0, 0, 1, 1, 1, 2, 2, 2 };
|
||||
|
||||
foreach (var (cell, overlay) in Overlay.IntersectsWith(locations).Where(o => o.Value.Type.IsResource))
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var facing in CellMetrics.AdjacentFacings)
|
||||
{
|
||||
var adjacentTiberium = Overlay.Adjacent(cell, facing);
|
||||
if (adjacentTiberium?.Type.IsResource ?? false)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
overlay.Icon = overlay.Type.IsGem ? gemCounts[count] : tiberiumCounts[count];
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateWallOverlays(ISet<Point> locations)
|
||||
{
|
||||
foreach (var (cell, overlay) in Overlay.IntersectsWith(locations).Where(o => o.Value.Type.IsWall))
|
||||
{
|
||||
var northWall = Overlay.Adjacent(cell, FacingType.North);
|
||||
var eastWall = Overlay.Adjacent(cell, FacingType.East);
|
||||
var southWall = Overlay.Adjacent(cell, FacingType.South);
|
||||
var westWall = Overlay.Adjacent(cell, FacingType.West);
|
||||
|
||||
int icon = 0;
|
||||
if (northWall?.Type == overlay.Type)
|
||||
{
|
||||
icon |= 1;
|
||||
}
|
||||
if (eastWall?.Type == overlay.Type)
|
||||
{
|
||||
icon |= 2;
|
||||
}
|
||||
if (southWall?.Type == overlay.Type)
|
||||
{
|
||||
icon |= 4;
|
||||
}
|
||||
if (westWall?.Type == overlay.Type)
|
||||
{
|
||||
icon |= 8;
|
||||
}
|
||||
|
||||
overlay.Icon = icon;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveBibs(Building building)
|
||||
{
|
||||
var bibCells = Smudge.IntersectsWith(building.BibCells).Where(x => (x.Value.Type.Flag & SmudgeTypeFlag.Bib) != SmudgeTypeFlag.None).Select(x => x.Cell).ToArray();
|
||||
foreach (var cell in bibCells)
|
||||
{
|
||||
Smudge[cell] = null;
|
||||
}
|
||||
building.BibCells.Clear();
|
||||
}
|
||||
|
||||
private void AddBibs(Point location, Building building)
|
||||
{
|
||||
if (!building.Type.HasBib)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var bib1Type = SmudgeTypes.Where(t => t.Flag == SmudgeTypeFlag.Bib1).FirstOrDefault();
|
||||
var bib2Type = SmudgeTypes.Where(t => t.Flag == SmudgeTypeFlag.Bib2).FirstOrDefault();
|
||||
var bib3Type = SmudgeTypes.Where(t => t.Flag == SmudgeTypeFlag.Bib3).FirstOrDefault();
|
||||
|
||||
SmudgeType bibType = null;
|
||||
switch (building.Type.Size.Width)
|
||||
{
|
||||
case 2:
|
||||
bibType = bib3Type;
|
||||
break;
|
||||
case 3:
|
||||
bibType = bib2Type;
|
||||
break;
|
||||
case 4:
|
||||
bibType = bib1Type;
|
||||
break;
|
||||
}
|
||||
if (bibType != null)
|
||||
{
|
||||
int icon = 0;
|
||||
for (var y = 0; y < bibType.Size.Height; ++y)
|
||||
{
|
||||
for (var x = 0; x < bibType.Size.Width; ++x, ++icon)
|
||||
{
|
||||
if (Metrics.GetCell(new Point(location.X + x, location.Y + building.Type.Size.Height + y - 1), out int subCell))
|
||||
{
|
||||
Smudge[subCell] = new Smudge
|
||||
{
|
||||
Type = bibType,
|
||||
Icon = icon,
|
||||
Data = 0,
|
||||
Tint = building.Tint
|
||||
};
|
||||
building.BibCells.Add(subCell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map Clone()
|
||||
{
|
||||
var map = new Map(BasicSection, Theater, Metrics.Size, HouseType,
|
||||
HouseTypes, TheaterTypes, TemplateTypes, TerrainTypes, OverlayTypes, SmudgeTypes,
|
||||
EventTypes, ActionTypes, MissionTypes, DirectionTypes, InfantryTypes, UnitTypes,
|
||||
BuildingTypes, TeamMissionTypes, Waypoints, MovieTypes)
|
||||
{
|
||||
TopLeft = TopLeft,
|
||||
Size = Size
|
||||
};
|
||||
|
||||
map.BeginUpdate();
|
||||
|
||||
MapSection.CopyTo(map.MapSection);
|
||||
BriefingSection.CopyTo(map.BriefingSection);
|
||||
SteamSection.CopyTo(map.SteamSection);
|
||||
Templates.CopyTo(map.Templates);
|
||||
Overlay.CopyTo(map.Overlay);
|
||||
Smudge.CopyTo(map.Smudge);
|
||||
CellTriggers.CopyTo(map.CellTriggers);
|
||||
Array.Copy(Houses, map.Houses, map.Houses.Length);
|
||||
|
||||
foreach (var trigger in Triggers)
|
||||
{
|
||||
map.Triggers.Add(trigger);
|
||||
}
|
||||
|
||||
foreach (var (location, occupier) in Technos)
|
||||
{
|
||||
if (occupier is InfantryGroup infantryGroup)
|
||||
{
|
||||
var newInfantryGroup = new InfantryGroup();
|
||||
Array.Copy(infantryGroup.Infantry, newInfantryGroup.Infantry, newInfantryGroup.Infantry.Length);
|
||||
map.Technos.Add(location, newInfantryGroup);
|
||||
}
|
||||
else if (!(occupier is Building))
|
||||
{
|
||||
map.Technos.Add(location, occupier);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (location, building) in Buildings)
|
||||
{
|
||||
map.Buildings.Add(location, building);
|
||||
}
|
||||
|
||||
map.TeamTypes.AddRange(TeamTypes);
|
||||
|
||||
map.EndUpdate();
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public TGA GeneratePreview(Size previewSize, bool sharpen)
|
||||
{
|
||||
var mapBounds = new Rectangle(
|
||||
Bounds.Left * Globals.OriginalTileWidth,
|
||||
Bounds.Top * Globals.OriginalTileHeight,
|
||||
Bounds.Width * Globals.OriginalTileWidth,
|
||||
Bounds.Height * Globals.OriginalTileHeight
|
||||
);
|
||||
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 croppedBitmap = new Bitmap(previewSize.Width, previewSize.Height))
|
||||
{
|
||||
var locations = Bounds.Points().ToHashSet();
|
||||
using (var g = Graphics.FromImage(fullBitmap))
|
||||
{
|
||||
MapRenderer.Render(GameType.None, this, g, locations, MapLayerFlag.Template | MapLayerFlag.Resources, 1);
|
||||
}
|
||||
|
||||
using (var g = Graphics.FromImage(croppedBitmap))
|
||||
{
|
||||
Matrix transform = new Matrix();
|
||||
transform.Scale(previewScale, previewScale);
|
||||
transform.Translate((scaledSize.Width - mapBounds.Width) / 2, (scaledSize.Height - mapBounds.Height) / 2);
|
||||
|
||||
g.Transform = transform;
|
||||
g.Clear(Color.Black);
|
||||
g.DrawImage(fullBitmap, new Rectangle(0, 0, mapBounds.Width, mapBounds.Height), mapBounds, GraphicsUnit.Pixel);
|
||||
}
|
||||
|
||||
fullBitmap.Dispose();
|
||||
|
||||
if (sharpen)
|
||||
{
|
||||
using (var sharpenedImage = croppedBitmap.Sharpen(1.0f))
|
||||
{
|
||||
croppedBitmap.Dispose();
|
||||
return TGA.FromBitmap(sharpenedImage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return TGA.FromBitmap(croppedBitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TGA GenerateMapPreview()
|
||||
{
|
||||
return GeneratePreview(Globals.MapPreviewSize, false);
|
||||
}
|
||||
|
||||
public TGA GenerateWorkshopPreview()
|
||||
{
|
||||
return GeneratePreview(Globals.WorkshopPreviewSize, true);
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
|
||||
private void Overlay_CellChanged(object sender, CellChangedEventArgs<Overlay> e)
|
||||
{
|
||||
if (e.OldValue?.Type.IsWall ?? false)
|
||||
{
|
||||
Buildings.Remove(e.OldValue);
|
||||
}
|
||||
|
||||
if (e.Value?.Type.IsWall ?? false)
|
||||
{
|
||||
Buildings.Add(e.Location, e.Value);
|
||||
}
|
||||
|
||||
if (updating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var overlay in new Overlay[] { e.OldValue, e.Value })
|
||||
{
|
||||
if (overlay == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MapLayerFlag layer = MapLayerFlag.None;
|
||||
if (overlay.Type.IsResource)
|
||||
{
|
||||
layer = MapLayerFlag.Resources;
|
||||
}
|
||||
else if (overlay.Type.IsWall)
|
||||
{
|
||||
layer = MapLayerFlag.Walls;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!invalidateLayers.TryGetValue(layer, out ISet<Point> locations))
|
||||
{
|
||||
locations = new HashSet<Point>();
|
||||
invalidateLayers[layer] = locations;
|
||||
}
|
||||
|
||||
locations.UnionWith(Rectangle.Inflate(new Rectangle(e.Location, new Size(1, 1)), 1, 1).Points());
|
||||
}
|
||||
|
||||
if (updateCount == 0)
|
||||
{
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
private void Technos_OccupierAdded(object sender, OccupierAddedEventArgs<ICellOccupier> e)
|
||||
{
|
||||
if (e.Occupier is ICellOverlapper overlapper)
|
||||
{
|
||||
if (updateCount == 0)
|
||||
{
|
||||
Overlappers.Add(e.Location, overlapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
invalidateOverlappers = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Technos_OccupierRemoved(object sender, OccupierRemovedEventArgs<ICellOccupier> e)
|
||||
{
|
||||
if (e.Occupier is ICellOverlapper overlapper)
|
||||
{
|
||||
if (updateCount == 0)
|
||||
{
|
||||
Overlappers.Remove(overlapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
invalidateOverlappers = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Buildings_OccupierAdded(object sender, OccupierAddedEventArgs<ICellOccupier> e)
|
||||
{
|
||||
if (e.Occupier is Building building)
|
||||
{
|
||||
Technos.Add(e.Location, e.Occupier, building.Type.BaseOccupyMask);
|
||||
AddBibs(e.Location, building);
|
||||
}
|
||||
else
|
||||
{
|
||||
Technos.Add(e.Location, e.Occupier);
|
||||
}
|
||||
}
|
||||
|
||||
private void Buildings_OccupierRemoved(object sender, OccupierRemovedEventArgs<ICellOccupier> e)
|
||||
{
|
||||
if (e.Occupier is Building building)
|
||||
{
|
||||
RemoveBibs(building);
|
||||
}
|
||||
|
||||
Technos.Remove(e.Occupier);
|
||||
}
|
||||
|
||||
private void Triggers_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
foreach (var (_, building) in Buildings.OfType<Building>())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(building.Trigger))
|
||||
{
|
||||
if (Triggers.Where(t => building.Trigger.Equals(t.Name)).FirstOrDefault() == null)
|
||||
{
|
||||
building.Trigger = Trigger.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user