C&C Remastered Map Editor
Initial commit of C&C Remastered Map Editor code
This commit is contained in:
parent
1f6350fe6e
commit
e37e174be1
289 changed files with 80922 additions and 7 deletions
185
CnCTDRAMapEditor/Model/BasicSection.cs
Normal file
185
CnCTDRAMapEditor/Model/BasicSection.cs
Normal file
|
@ -0,0 +1,185 @@
|
|||
//
|
||||
// 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.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class BooleanTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return (context is MapContext) && (sourceType == typeof(string));
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return (context is MapContext) && (destinationType == typeof(string));
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (!(value is bool boolean) || !CanConvertTo(context, destinationType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return boolean ? "1" : "0";
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (!(value is string str) || !CanConvertFrom(context, value?.GetType()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var first = (str.Length > 0) ? str.ToUpper()[0] : 0;
|
||||
return (first == 'T') || (first == 'Y') || (first == '1');
|
||||
}
|
||||
}
|
||||
|
||||
public class PercentageTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return (context is MapContext) && (sourceType == typeof(string));
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return (context is MapContext) && (destinationType == typeof(string));
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (!(value is int percent) || !CanConvertTo(context, destinationType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var mapContext = context as MapContext;
|
||||
return mapContext.FractionalPercentages ? (percent / 100M).ToString("D2") : percent.ToString();
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (!(value is string str) || !CanConvertFrom(context, value?.GetType()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var mapContext = context as MapContext;
|
||||
if (mapContext.FractionalPercentages && str.Contains("."))
|
||||
{
|
||||
if (!decimal.TryParse(str, out decimal percent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return (int)(percent * 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!int.TryParse(str, out int percent))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return percent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BasicSection : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private string name;
|
||||
[DefaultValue(null)]
|
||||
public string Name { get => name; set => SetField(ref name, value); }
|
||||
|
||||
private int carryOverCap;
|
||||
[TypeConverter(typeof(PercentageTypeConverter))]
|
||||
[DefaultValue(-1)]
|
||||
public int CarryOverCap { get => carryOverCap; set => SetField(ref carryOverCap, value); }
|
||||
|
||||
private int carryOverMoney;
|
||||
[TypeConverter(typeof(PercentageTypeConverter))]
|
||||
[DefaultValue(100)]
|
||||
public int CarryOverMoney { get => carryOverMoney; set => SetField(ref carryOverMoney, value); }
|
||||
|
||||
private string intro;
|
||||
[DefaultValue(null)]
|
||||
public string Intro { get => intro; set => SetField(ref intro, value); }
|
||||
|
||||
private string theme;
|
||||
[DefaultValue("No Theme")]
|
||||
public string Theme { get => theme; set => SetField(ref theme, value); }
|
||||
|
||||
private int percent;
|
||||
[TypeConverter(typeof(PercentageTypeConverter))]
|
||||
[DefaultValue(100)]
|
||||
public int Percent { get => percent; set => SetField(ref percent, value); }
|
||||
|
||||
public string player;
|
||||
[DefaultValue(null)]
|
||||
public string Player { get => player; set => SetField(ref player, value); }
|
||||
|
||||
private string action;
|
||||
[DefaultValue("x")]
|
||||
public string Action { get => action; set => SetField(ref action, value); }
|
||||
|
||||
private string lose;
|
||||
[DefaultValue("x")]
|
||||
public string Lose { get => lose; set => SetField(ref lose, value); }
|
||||
|
||||
private string win;
|
||||
[DefaultValue("x")]
|
||||
public string Win { get => win; set => SetField(ref win, value); }
|
||||
|
||||
private string brief;
|
||||
[DefaultValue("x")]
|
||||
public string Brief { get => brief; set => SetField(ref brief, value); }
|
||||
|
||||
private string author;
|
||||
[DefaultValue(null)]
|
||||
public string Author { get => author; set => SetField(ref author, value); }
|
||||
|
||||
private string basePlayer;
|
||||
[NonSerializedINIKey]
|
||||
[DefaultValue(null)]
|
||||
public string BasePlayer { get => basePlayer; set => SetField(ref basePlayer, value); }
|
||||
|
||||
private bool soloMission;
|
||||
[DefaultValue(false)]
|
||||
public bool SoloMission { get => soloMission; set => SetField(ref soloMission, value); }
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
24
CnCTDRAMapEditor/Model/BriefingSection.cs
Normal file
24
CnCTDRAMapEditor/Model/BriefingSection.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
//
|
||||
// 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 System.ComponentModel;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class BriefingSection
|
||||
{
|
||||
[DefaultValue(null)]
|
||||
public string Briefing { get; set; }
|
||||
}
|
||||
}
|
103
CnCTDRAMapEditor/Model/Building.cs
Normal file
103
CnCTDRAMapEditor/Model/Building.cs
Normal file
|
@ -0,0 +1,103 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class Building : ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private BuildingType type;
|
||||
public BuildingType Type { get => type; set => SetField(ref type, value); }
|
||||
|
||||
public Rectangle OverlapBounds => Type.OverlapBounds;
|
||||
|
||||
public bool[,] OccupyMask => Type.OccupyMask;
|
||||
|
||||
private HouseType house;
|
||||
public HouseType House { get => house; set => SetField(ref house, value); }
|
||||
|
||||
private int strength;
|
||||
public int Strength { get => strength; set => SetField(ref strength, value); }
|
||||
|
||||
private DirectionType direction;
|
||||
public DirectionType Direction { get => direction; set => SetField(ref direction, value); }
|
||||
|
||||
private string trigger = Model.Trigger.None;
|
||||
public string Trigger { get => trigger; set => SetField(ref trigger, value); }
|
||||
|
||||
private int basePriority = -1;
|
||||
public int BasePriority { get => basePriority; set => SetField(ref basePriority, value); }
|
||||
|
||||
private bool isPrebuilt = true;
|
||||
public bool IsPrebuilt { get => isPrebuilt; set => SetField(ref isPrebuilt, value); }
|
||||
|
||||
private bool sellable;
|
||||
public bool Sellable { get => sellable; set => SetField(ref sellable, value); }
|
||||
|
||||
private bool rebuild;
|
||||
public bool Rebuild { get => rebuild; set => SetField(ref rebuild, value); }
|
||||
|
||||
public ISet<int> BibCells { get; private set; } = new HashSet<int>();
|
||||
|
||||
private Color tint = Color.White;
|
||||
public Color Tint
|
||||
{
|
||||
get => IsPrebuilt ? tint : Color.FromArgb((int)(tint.A * 0.75f), tint.R, tint.G, tint.B);
|
||||
set => tint = value;
|
||||
}
|
||||
|
||||
public Building Clone()
|
||||
{
|
||||
return new Building()
|
||||
{
|
||||
Type = Type,
|
||||
House = House,
|
||||
Strength = Strength,
|
||||
Direction = Direction,
|
||||
Trigger = Trigger,
|
||||
BasePriority = BasePriority,
|
||||
IsPrebuilt = IsPrebuilt,
|
||||
Sellable = Sellable,
|
||||
Rebuild = Rebuild,
|
||||
Tint = Tint
|
||||
};
|
||||
}
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
}
|
166
CnCTDRAMapEditor/Model/BuildingType.cs
Normal file
166
CnCTDRAMapEditor/Model/BuildingType.cs
Normal file
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class BuildingType : ICellOverlapper, ICellOccupier, ITechnoType, IBrowsableType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
public string Tilename { get; private set; }
|
||||
|
||||
public Rectangle OverlapBounds => new Rectangle(Point.Empty, new Size(OccupyMask.GetLength(1), OccupyMask.GetLength(0)));
|
||||
|
||||
public bool[,] OccupyMask { get; private set; }
|
||||
|
||||
public bool[,] BaseOccupyMask { get; private set; }
|
||||
|
||||
public Size Size { get; private set; }
|
||||
|
||||
public bool HasBib { get; private set; }
|
||||
|
||||
public string OwnerHouse { get; private set; }
|
||||
|
||||
public TheaterType[] Theaters { get; private set; }
|
||||
|
||||
public bool IsFake { get; private set; }
|
||||
|
||||
public bool HasTurret { get; private set; }
|
||||
|
||||
public string FactoryOverlay { get; private set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public BuildingType(sbyte id, string name, string textId, bool[,] occupyMask, bool hasBib, string ownerHouse, TheaterType[] theaters, bool isFake, bool hasTurret, string factoryOverlay)
|
||||
{
|
||||
ID = id;
|
||||
Name = isFake ? (name.Substring(0, name.Length - 1) + "f") : name;
|
||||
DisplayName = Globals.TheGameTextManager[textId];
|
||||
Tilename = name;
|
||||
BaseOccupyMask = occupyMask;
|
||||
Size = new Size(BaseOccupyMask.GetLength(1), BaseOccupyMask.GetLength(0));
|
||||
HasBib = hasBib;
|
||||
OwnerHouse = ownerHouse;
|
||||
Theaters = theaters;
|
||||
IsFake = isFake;
|
||||
HasTurret = hasTurret;
|
||||
FactoryOverlay = factoryOverlay;
|
||||
|
||||
if (HasBib)
|
||||
{
|
||||
OccupyMask = new bool[BaseOccupyMask.GetLength(0) + 1, BaseOccupyMask.GetLength(1)];
|
||||
for (var i = 0; i < BaseOccupyMask.GetLength(0) - 1; ++i)
|
||||
{
|
||||
for (var j = 0; j < BaseOccupyMask.GetLength(1); ++j)
|
||||
{
|
||||
OccupyMask[i, j] = BaseOccupyMask[i, j];
|
||||
}
|
||||
}
|
||||
for (var j = 0; j < OccupyMask.GetLength(1); ++j)
|
||||
{
|
||||
OccupyMask[OccupyMask.GetLength(0) - 2, j] = true;
|
||||
OccupyMask[OccupyMask.GetLength(0) - 1, j] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OccupyMask = BaseOccupyMask;
|
||||
}
|
||||
}
|
||||
|
||||
public BuildingType(sbyte id, string name, string textId, bool[,] occupyMask, bool hasBib, string ownerHouse, bool isFake, bool hasTurret, string factoryOverlay)
|
||||
: this(id, name, textId, occupyMask, hasBib, ownerHouse, null, isFake, hasTurret, factoryOverlay)
|
||||
{
|
||||
}
|
||||
|
||||
public BuildingType(sbyte id, string name, string textId, bool[,] occupyMask, bool hasBib, string ownerHouse)
|
||||
: this(id, name, textId, occupyMask, hasBib, ownerHouse, null, false, false, null)
|
||||
{
|
||||
}
|
||||
|
||||
public BuildingType(sbyte id, string name, string textId, bool[,] occupyMask, bool hasBib, string ownerHouse, TheaterType[] theaters)
|
||||
: this(id, name, textId, occupyMask, hasBib, ownerHouse, theaters, false, false, null)
|
||||
{
|
||||
}
|
||||
|
||||
public BuildingType(sbyte id, string name, string textId, bool[,] occupyMask, bool hasBib, string ownerHouse, bool isFake)
|
||||
: this(id, name, textId, occupyMask, hasBib, ownerHouse, null, isFake, false, null)
|
||||
{
|
||||
}
|
||||
|
||||
public BuildingType(sbyte id, string name, string textId, bool[,] occupyMask, bool hasBib, string ownerHouse, bool isFake, bool hasTurret)
|
||||
: this(id, name, textId, occupyMask, hasBib, ownerHouse, null, isFake, hasTurret, null)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is BuildingType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(GameType gameType, TheaterType theater, HouseType house, DirectionType direction)
|
||||
{
|
||||
var mockBuilding = new Building()
|
||||
{
|
||||
Type = this,
|
||||
House = house,
|
||||
Strength = 256,
|
||||
Direction = direction
|
||||
};
|
||||
|
||||
var render = MapRenderer.Render(gameType, theater, Point.Empty, Globals.TileSize, Globals.TileScale, mockBuilding);
|
||||
if (!render.Item1.IsEmpty)
|
||||
{
|
||||
var buildingPreview = new Bitmap(render.Item1.Width, render.Item1.Height);
|
||||
using (var g = Graphics.FromImage(buildingPreview))
|
||||
{
|
||||
render.Item2(g);
|
||||
}
|
||||
Thumbnail = buildingPreview;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
193
CnCTDRAMapEditor/Model/CellGrid.cs
Normal file
193
CnCTDRAMapEditor/Model/CellGrid.cs
Normal file
|
@ -0,0 +1,193 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public enum FacingType
|
||||
{
|
||||
None,
|
||||
North,
|
||||
NorthEast,
|
||||
East,
|
||||
SouthEast,
|
||||
South,
|
||||
SouthWest,
|
||||
West,
|
||||
NorthWest
|
||||
}
|
||||
|
||||
public class CellChangedEventArgs<T> : EventArgs
|
||||
{
|
||||
public readonly int Cell;
|
||||
|
||||
public readonly Point Location;
|
||||
|
||||
public readonly T OldValue;
|
||||
|
||||
public readonly T Value;
|
||||
|
||||
public CellChangedEventArgs(CellMetrics metrics, int cell, T oldValue, T value)
|
||||
{
|
||||
Cell = cell;
|
||||
metrics.GetLocation(cell, out Location);
|
||||
OldValue = oldValue;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public CellChangedEventArgs(CellMetrics metrics, Point location, T oldValue, T value)
|
||||
{
|
||||
Location = location;
|
||||
metrics.GetCell(location, out Cell);
|
||||
OldValue = oldValue;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public class CellGrid<T> : IEnumerable<(int Cell, T Value)>, IEnumerable
|
||||
{
|
||||
private readonly CellMetrics metrics;
|
||||
private readonly T[,] cells;
|
||||
|
||||
public T this[int x, int y]
|
||||
{
|
||||
get => cells[y, x];
|
||||
set
|
||||
{
|
||||
if (!EqualityComparer<T>.Default.Equals(cells[y, x], value))
|
||||
{
|
||||
var lastValue = cells[y, x];
|
||||
cells[y, x] = value;
|
||||
OnCellChanged(new CellChangedEventArgs<T>(metrics, new Point(x, y), lastValue, cells[y, x]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public T this[Point location] { get => this[location.X, location.Y]; set => this[location.X, location.Y] = value; }
|
||||
|
||||
public T this[int cell] { get => this[cell % metrics.Width, cell / metrics.Width]; set => this[cell % metrics.Width, cell / metrics.Width] = value; }
|
||||
|
||||
public Size Size => metrics.Size;
|
||||
|
||||
public int Length => metrics.Length;
|
||||
|
||||
public event EventHandler<CellChangedEventArgs<T>> CellChanged;
|
||||
public event EventHandler<EventArgs> Cleared;
|
||||
|
||||
public CellGrid(CellMetrics metrics)
|
||||
{
|
||||
this.metrics = metrics;
|
||||
|
||||
cells = new T[metrics.Height, metrics.Width];
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(cells, 0, cells.Length);
|
||||
OnCleared();
|
||||
}
|
||||
|
||||
public T Adjacent(Point location, FacingType facing)
|
||||
{
|
||||
return metrics.Adjacent(location, facing, out Point adjacent) ? this[adjacent] : default;
|
||||
}
|
||||
|
||||
public T Adjacent(int cell, FacingType facing)
|
||||
{
|
||||
if (!metrics.GetLocation(cell, out Point location))
|
||||
{
|
||||
return default;
|
||||
|
||||
}
|
||||
return metrics.Adjacent(location, facing, out Point adjacent) ? this[adjacent] : default;
|
||||
}
|
||||
|
||||
public bool CopyTo(CellGrid<T> other)
|
||||
{
|
||||
if (metrics.Length != other.metrics.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < metrics.Length; ++i)
|
||||
{
|
||||
other[i] = this[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void OnCellChanged(CellChangedEventArgs<T> e)
|
||||
{
|
||||
CellChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnCleared()
|
||||
{
|
||||
Cleared?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
public IEnumerable<(int Cell, T Value)> IntersectsWith(ISet<int> cells)
|
||||
{
|
||||
foreach (var i in cells)
|
||||
{
|
||||
if (metrics.Contains(i))
|
||||
{
|
||||
var cell = this[i];
|
||||
if (cell != null)
|
||||
{
|
||||
yield return (i, cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<(int Cell, T Value)> IntersectsWith(ISet<Point> locations)
|
||||
{
|
||||
foreach (var location in locations)
|
||||
{
|
||||
if (metrics.Contains(location))
|
||||
{
|
||||
var cell = this[location];
|
||||
if (cell != null)
|
||||
{
|
||||
metrics.GetCell(location, out int i);
|
||||
yield return (i, cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(int Cell, T Value)> GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < metrics.Length; ++i)
|
||||
{
|
||||
var cell = this[i];
|
||||
if (cell != null)
|
||||
{
|
||||
yield return (i, cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
137
CnCTDRAMapEditor/Model/CellMetrics.cs
Normal file
137
CnCTDRAMapEditor/Model/CellMetrics.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class CellMetrics
|
||||
{
|
||||
public static readonly FacingType[] AdjacentFacings = new FacingType[] {
|
||||
FacingType.North, FacingType.NorthEast, FacingType.East, FacingType.SouthEast, FacingType.South, FacingType.SouthWest, FacingType.West, FacingType.NorthWest
|
||||
};
|
||||
|
||||
public int Width { get; private set; }
|
||||
|
||||
public int Height { get; private set; }
|
||||
|
||||
public Point TopLeft => Point.Empty;
|
||||
|
||||
public Size Size => new Size(Width, Height);
|
||||
|
||||
public Rectangle Bounds => new Rectangle(TopLeft, Size);
|
||||
|
||||
public int Length => Width * Height;
|
||||
|
||||
public bool Contains(Point location) => ((location.X >= 0) && (location.X < Width) && (location.Y >= 0) && (location.Y < Height));
|
||||
|
||||
public bool Contains(int cell) => ((cell >= 0) && (cell < Length));
|
||||
|
||||
public CellMetrics(int width, int height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public CellMetrics(Size size)
|
||||
: this(size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public bool GetCell(Point location, out int cell)
|
||||
{
|
||||
cell = (location.Y * Width) + location.X;
|
||||
return Contains(location);
|
||||
}
|
||||
|
||||
public bool GetLocation(int cell, out Point location)
|
||||
{
|
||||
location = new Point(cell % Width, cell / Width);
|
||||
return Contains(cell);
|
||||
}
|
||||
|
||||
public bool Adjacent(Point location, FacingType facing, out Point adjacent)
|
||||
{
|
||||
adjacent = location;
|
||||
switch (facing)
|
||||
{
|
||||
case FacingType.North:
|
||||
adjacent.Y--;
|
||||
break;
|
||||
case FacingType.NorthEast:
|
||||
adjacent.X++;
|
||||
adjacent.Y--;
|
||||
break;
|
||||
case FacingType.East:
|
||||
adjacent.X++;
|
||||
break;
|
||||
case FacingType.SouthEast:
|
||||
adjacent.X++;
|
||||
adjacent.Y++;
|
||||
break;
|
||||
case FacingType.South:
|
||||
adjacent.Y++;
|
||||
break;
|
||||
case FacingType.SouthWest:
|
||||
adjacent.X--;
|
||||
adjacent.Y++;
|
||||
break;
|
||||
case FacingType.West:
|
||||
adjacent.X--;
|
||||
break;
|
||||
case FacingType.NorthWest:
|
||||
adjacent.X--;
|
||||
adjacent.Y--;
|
||||
break;
|
||||
}
|
||||
|
||||
return Contains(adjacent);
|
||||
}
|
||||
|
||||
public bool Adjacent(int cell, FacingType facing, out int adjacent)
|
||||
{
|
||||
if (!GetLocation(cell, out Point location) || !Adjacent(location, facing, out Point adjacentPoint))
|
||||
{
|
||||
adjacent = -1;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetCell(adjacentPoint, out adjacent);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clip(ref Point location)
|
||||
{
|
||||
location.X = Math.Max(0, Math.Min(Width - 1, location.X));
|
||||
location.Y = Math.Max(0, Math.Min(Height - 1, location.Y));
|
||||
}
|
||||
|
||||
public void Clip(ref Point location, Size margin)
|
||||
{
|
||||
Clip(ref location, margin, margin);
|
||||
}
|
||||
|
||||
public void Clip(ref Point location, Size topLeftMargin, Size bottomRightMargin)
|
||||
{
|
||||
location.X = Math.Max(topLeftMargin.Width, Math.Min(Width - bottomRightMargin.Width - 1, location.X));
|
||||
location.Y = Math.Max(topLeftMargin.Height, Math.Min(Height - bottomRightMargin.Height - 1, location.Y));
|
||||
}
|
||||
}
|
||||
}
|
21
CnCTDRAMapEditor/Model/CellTrigger.cs
Normal file
21
CnCTDRAMapEditor/Model/CellTrigger.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// 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
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class CellTrigger
|
||||
{
|
||||
public string Trigger { get; set; } = Model.Trigger.None;
|
||||
}
|
||||
}
|
71
CnCTDRAMapEditor/Model/DirectionType.cs
Normal file
71
CnCTDRAMapEditor/Model/DirectionType.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// 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 System;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class DirectionType
|
||||
{
|
||||
public byte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public FacingType Facing { get; private set; }
|
||||
|
||||
public DirectionType(byte id, string name, FacingType facing)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
Facing = facing;
|
||||
}
|
||||
|
||||
public DirectionType(byte id, string name)
|
||||
: this(id, name, FacingType.None)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is DirectionType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is byte)
|
||||
{
|
||||
return ID == (byte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
else if (obj is FacingType)
|
||||
{
|
||||
return Facing == (FacingType)obj;
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
172
CnCTDRAMapEditor/Model/House.cs
Normal file
172
CnCTDRAMapEditor/Model/House.cs
Normal file
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// 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.Utility;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public struct AlliesMask : IEnumerable<int>
|
||||
{
|
||||
public int Value { get; private set; }
|
||||
|
||||
public AlliesMask(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public void Set(int id)
|
||||
{
|
||||
if ((id < 0) || (id > 31))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
Value |= (1 << id);
|
||||
}
|
||||
|
||||
public void Clear(int id)
|
||||
{
|
||||
if ((id < 0) || (id > 31))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
Value &= ~(1 << id);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Value.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value.ToString();
|
||||
}
|
||||
|
||||
public IEnumerator<int> GetEnumerator()
|
||||
{
|
||||
for (int i = 0, mask = 1; i < 32; ++i, mask <<= 1)
|
||||
{
|
||||
if ((Value & mask) != 0)
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
public class AlliesMaskTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return (context is MapContext) && (sourceType == typeof(string));
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return (context is MapContext) && (destinationType == typeof(string));
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (!(value is AlliesMask) || !CanConvertTo(context, destinationType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var map = (context as MapContext).Map;
|
||||
var alliesMask = (AlliesMask)value;
|
||||
|
||||
var allies = new List<string>(map.Houses.Length);
|
||||
foreach (var id in alliesMask)
|
||||
{
|
||||
if (map.Houses.Where(h => h.Type.Equals((sbyte)id)).FirstOrDefault() is House house)
|
||||
{
|
||||
allies.Add(house.Type.Name);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join(",", allies);
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (!CanConvertFrom(context, value?.GetType()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var map = (context as MapContext).Instance as Map;
|
||||
var alliesMask = new AlliesMask(0);
|
||||
|
||||
var allies = (value as string).Split(',');
|
||||
foreach (var ally in allies)
|
||||
{
|
||||
if (map.Houses.Where(h => h.Type.Equals(ally)).FirstOrDefault() is House house)
|
||||
{
|
||||
alliesMask.Set(house.Type.ID);
|
||||
}
|
||||
}
|
||||
|
||||
return alliesMask;
|
||||
}
|
||||
}
|
||||
|
||||
public class House
|
||||
{
|
||||
public readonly HouseType Type;
|
||||
|
||||
[NonSerializedINIKey]
|
||||
[DefaultValue(true)]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
[TypeConverter(typeof(AlliesMaskTypeConverter))]
|
||||
public AlliesMask Allies { get; set; }
|
||||
|
||||
[DefaultValue(150)]
|
||||
public int MaxBuilding { get; set; }
|
||||
|
||||
[DefaultValue(150)]
|
||||
public int MaxUnit { get; set; }
|
||||
|
||||
[DefaultValue("North")]
|
||||
public string Edge { get; set; }
|
||||
|
||||
[DefaultValue(0)]
|
||||
public int Credits { get; set; } = 0;
|
||||
|
||||
public House(HouseType type)
|
||||
{
|
||||
Type = type;
|
||||
Allies = new AlliesMask(1 << Type.ID);
|
||||
}
|
||||
}
|
||||
}
|
82
CnCTDRAMapEditor/Model/HouseType.cs
Normal file
82
CnCTDRAMapEditor/Model/HouseType.cs
Normal file
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class HouseType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string UnitTeamColor { get; private set; }
|
||||
|
||||
public string BuildingTeamColor { get; private set; }
|
||||
|
||||
public IDictionary<string, string> OverrideTeamColors { get; private set; }
|
||||
|
||||
public HouseType(sbyte id, string name, string unitTeamColor, string buildingTeamColor, params (string type, string teamColor)[] overrideTeamColors)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
UnitTeamColor = unitTeamColor;
|
||||
BuildingTeamColor = buildingTeamColor;
|
||||
OverrideTeamColors = overrideTeamColors.ToDictionary(x => x.type, x => x.teamColor);
|
||||
}
|
||||
|
||||
public HouseType(sbyte id, string name, string teamColor)
|
||||
: this(id, name, teamColor, teamColor)
|
||||
{
|
||||
}
|
||||
|
||||
public HouseType(sbyte id, string name)
|
||||
: this(id, name, null)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is HouseType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
127
CnCTDRAMapEditor/Model/Infantry.cs
Normal file
127
CnCTDRAMapEditor/Model/Infantry.cs
Normal file
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public enum InfantryStoppingType
|
||||
{
|
||||
Center = 0,
|
||||
UpperLeft = 1,
|
||||
UpperRight = 2,
|
||||
LowerLeft = 3,
|
||||
LowerRight = 4
|
||||
}
|
||||
|
||||
public class Infantry : INotifyPropertyChanged, ICloneable
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public InfantryGroup InfantryGroup { get; set; }
|
||||
|
||||
private InfantryType type;
|
||||
public InfantryType Type { get => type; set => SetField(ref type, value); }
|
||||
|
||||
private HouseType house;
|
||||
public HouseType House { get => house; set => SetField(ref house, value); }
|
||||
|
||||
private int strength;
|
||||
public int Strength { get => strength; set => SetField(ref strength, value); }
|
||||
|
||||
private DirectionType direction;
|
||||
public DirectionType Direction { get => direction; set => SetField(ref direction, value); }
|
||||
|
||||
private string mission;
|
||||
public string Mission { get => mission; set => SetField(ref mission, value); }
|
||||
|
||||
private string trigger = Model.Trigger.None;
|
||||
public string Trigger { get => trigger; set => SetField(ref trigger, value); }
|
||||
|
||||
public Color Tint { get; set; } = Color.White;
|
||||
|
||||
public Infantry(InfantryGroup infantryGroup)
|
||||
{
|
||||
InfantryGroup = infantryGroup;
|
||||
}
|
||||
|
||||
public Infantry Clone()
|
||||
{
|
||||
return new Infantry(InfantryGroup)
|
||||
{
|
||||
Type = Type,
|
||||
House = House,
|
||||
Strength = Strength,
|
||||
Direction = Direction,
|
||||
Trigger = Trigger,
|
||||
Mission = Mission,
|
||||
};
|
||||
}
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public class InfantryGroup : ICellOverlapper, ICellOccupier
|
||||
{
|
||||
private static readonly Point[] stoppingLocations = new Point[Globals.NumInfantryStops];
|
||||
|
||||
public Rectangle OverlapBounds => new Rectangle(-1, -1, 3, 3);
|
||||
|
||||
public bool[,] OccupyMask => new bool[1, 1] { { true } };
|
||||
|
||||
public readonly Infantry[] Infantry = new Infantry[Globals.NumInfantryStops];
|
||||
|
||||
static InfantryGroup()
|
||||
{
|
||||
stoppingLocations[(int)InfantryStoppingType.Center] = new Point(Globals.PixelWidth / 2, Globals.PixelHeight / 2);
|
||||
stoppingLocations[(int)InfantryStoppingType.UpperLeft] = new Point(Globals.PixelWidth / 4, Globals.PixelHeight / 4);
|
||||
stoppingLocations[(int)InfantryStoppingType.UpperRight] = new Point(3 * Globals.PixelWidth / 4, Globals.PixelHeight / 4);
|
||||
stoppingLocations[(int)InfantryStoppingType.LowerLeft] = new Point(Globals.PixelWidth / 4, 3 * Globals.PixelHeight / 4);
|
||||
stoppingLocations[(int)InfantryStoppingType.LowerRight] = new Point(3 * Globals.PixelWidth / 4, 3 * Globals.PixelHeight / 4);
|
||||
}
|
||||
|
||||
public static IEnumerable<InfantryStoppingType> ClosestStoppingTypes(Point subPixel)
|
||||
{
|
||||
var stoppingDistances = new (InfantryStoppingType type, float dist)[stoppingLocations.Length];
|
||||
for (int i = 0; i < stoppingDistances.Length; ++i)
|
||||
{
|
||||
stoppingDistances[i] = ((InfantryStoppingType)i, new Vector2(subPixel.X - stoppingLocations[i].X, subPixel.Y - stoppingLocations[i].Y).LengthSquared());
|
||||
}
|
||||
return stoppingDistances.OrderBy(sd => sd.dist).Select(sd => sd.type);
|
||||
}
|
||||
}
|
||||
}
|
100
CnCTDRAMapEditor/Model/InfantryType.cs
Normal file
100
CnCTDRAMapEditor/Model/InfantryType.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// 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.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class InfantryType : ITechnoType, IBrowsableType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
public string OwnerHouse { get; private set; }
|
||||
|
||||
public Size RenderSize { get; set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public InfantryType(sbyte id, string name, string textId, string ownerHouse)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
DisplayName = Globals.TheGameTextManager[textId];
|
||||
OwnerHouse = ownerHouse;
|
||||
}
|
||||
|
||||
public InfantryType(sbyte id, string name, string textId)
|
||||
: this(id, name, textId, null)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is InfantryType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(GameType gameType, TheaterType theater, HouseType house, DirectionType direction)
|
||||
{
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, 4, out Tile tile))
|
||||
{
|
||||
RenderSize = new Size(tile.Image.Width / Globals.TileScale, tile.Image.Height / Globals.TileScale);
|
||||
}
|
||||
|
||||
var mockInfantry = new Infantry(null)
|
||||
{
|
||||
Type = this,
|
||||
House = house,
|
||||
Strength = 256,
|
||||
Direction = direction
|
||||
};
|
||||
var infantryThumbnail = new Bitmap(Globals.TileWidth, Globals.TileHeight);
|
||||
using (var g = Graphics.FromImage(infantryThumbnail))
|
||||
{
|
||||
MapRenderer.Render(theater, Point.Empty, Globals.TileSize, mockInfantry, InfantryStoppingType.Center).Item2(g);
|
||||
}
|
||||
Thumbnail = infantryThumbnail;
|
||||
}
|
||||
}
|
||||
}
|
681
CnCTDRAMapEditor/Model/Map.cs
Normal file
681
CnCTDRAMapEditor/Model/Map.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
96
CnCTDRAMapEditor/Model/MapSection.cs
Normal file
96
CnCTDRAMapEditor/Model/MapSection.cs
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class TheaterTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return (context is MapContext) && (sourceType == typeof(string));
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||
{
|
||||
return (context is MapContext) && (destinationType == typeof(string));
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||
{
|
||||
if (!(value is TheaterType) || !CanConvertTo(context, destinationType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return (value as TheaterType)?.Name;
|
||||
}
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
if (!CanConvertFrom(context, value?.GetType()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var map = (context as MapContext).Map;
|
||||
return map.TheaterTypes.Where(t => t.Equals(value)).FirstOrDefault() ?? map.TheaterTypes.First();
|
||||
}
|
||||
}
|
||||
|
||||
public class MapSection : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private int x;
|
||||
[DefaultValue(0)]
|
||||
public int X { get => x; set => SetField(ref x, value); }
|
||||
|
||||
private int y;
|
||||
[DefaultValue(0)]
|
||||
public int Y { get => y; set => SetField(ref y, value); }
|
||||
|
||||
private int width;
|
||||
[DefaultValue(0)]
|
||||
public int Width { get => width; set => SetField(ref width, value); }
|
||||
|
||||
private int height;
|
||||
[DefaultValue(0)]
|
||||
public int Height { get => height; set => SetField(ref height, value); }
|
||||
|
||||
private TheaterType theater;
|
||||
[TypeConverter(typeof(TheaterTypeConverter))]
|
||||
[DefaultValue(null)]
|
||||
public TheaterType Theater { get => theater; set => SetField(ref theater, value); }
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
250
CnCTDRAMapEditor/Model/OccupierSet.cs
Normal file
250
CnCTDRAMapEditor/Model/OccupierSet.cs
Normal file
|
@ -0,0 +1,250 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class OccupierAddedEventArgs<T> : EventArgs
|
||||
{
|
||||
public readonly int Cell;
|
||||
|
||||
public readonly Point Location;
|
||||
|
||||
public readonly T Occupier;
|
||||
|
||||
public OccupierAddedEventArgs(CellMetrics metrics, int cell, T occupier)
|
||||
{
|
||||
Cell = cell;
|
||||
metrics.GetLocation(cell, out Location);
|
||||
Occupier = occupier;
|
||||
}
|
||||
|
||||
public OccupierAddedEventArgs(CellMetrics metrics, Point location, T occupier)
|
||||
{
|
||||
Location = location;
|
||||
metrics.GetCell(location, out Cell);
|
||||
Occupier = occupier;
|
||||
}
|
||||
}
|
||||
|
||||
public class OccupierRemovedEventArgs<T> : EventArgs
|
||||
{
|
||||
public readonly int Cell;
|
||||
|
||||
public readonly Point Location;
|
||||
|
||||
public readonly T Occupier;
|
||||
|
||||
public OccupierRemovedEventArgs(CellMetrics metrics, int cell, T occupier)
|
||||
{
|
||||
Cell = cell;
|
||||
metrics.GetLocation(cell, out Location);
|
||||
Occupier = occupier;
|
||||
}
|
||||
|
||||
public OccupierRemovedEventArgs(CellMetrics metrics, Point location, T occupier)
|
||||
{
|
||||
Location = location;
|
||||
metrics.GetCell(location, out Cell);
|
||||
Occupier = occupier;
|
||||
}
|
||||
}
|
||||
|
||||
public class OccupierSet<T> : IEnumerable<(Point Location, T Occupier)>, IEnumerable where T : class, ICellOccupier
|
||||
{
|
||||
private readonly CellMetrics metrics;
|
||||
private readonly Dictionary<T, Point> occupiers = new Dictionary<T, Point>();
|
||||
private readonly T[,] occupierCells;
|
||||
|
||||
public T this[Point location] => this[location.X, location.Y];
|
||||
|
||||
public T this[int x, int y] => Contains(x, y) ? occupierCells[y, x] : null;
|
||||
|
||||
public T this[int cell] => metrics.GetLocation(cell, out Point location) ? this[location] : null;
|
||||
|
||||
public Point? this[T occupier] => occupiers.ContainsKey(occupier) ? occupiers[occupier] : default;
|
||||
|
||||
public IEnumerable<T> Occupiers => occupiers.Keys;
|
||||
|
||||
public event EventHandler<OccupierAddedEventArgs<T>> OccupierAdded;
|
||||
public event EventHandler<OccupierRemovedEventArgs<T>> OccupierRemoved;
|
||||
public event EventHandler<EventArgs> Cleared;
|
||||
|
||||
public OccupierSet(CellMetrics metrics)
|
||||
{
|
||||
this.metrics = metrics;
|
||||
occupierCells = new T[metrics.Height, metrics.Width];
|
||||
}
|
||||
|
||||
public bool CanAdd(Point location, T occupier, bool[,] occupyMask)
|
||||
{
|
||||
if ((occupier == null) || Contains(occupier))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var occupyPoints = GetOccupyPoints(location, occupyMask).ToArray();
|
||||
return !occupyPoints.Any(p => !Contains(p) || (this[p] != null));
|
||||
}
|
||||
|
||||
public bool CanAdd(int x, int y, T occupier, bool[,] occupyMask) => CanAdd(new Point(x, y), occupier, occupyMask);
|
||||
|
||||
public bool CanAdd(int cell, T occupier, bool[,] occupyMask) => metrics.GetLocation(cell, out Point location) ? CanAdd(location, occupier, occupyMask) : false;
|
||||
|
||||
public bool CanAdd(Point location, T occupier) => (occupier != null) ? CanAdd(location, occupier, occupier.OccupyMask) : false;
|
||||
|
||||
public bool CanAdd(int x, int y, T occupier) => (occupier != null) ? CanAdd(x, y, occupier, occupier.OccupyMask) : false;
|
||||
|
||||
public bool CanAdd(int cell, T occupier) => (occupier != null) ? CanAdd(cell, occupier, occupier.OccupyMask) : false;
|
||||
|
||||
public bool Add(Point location, T occupier, bool[,] occupyMask)
|
||||
{
|
||||
if (!DoAdd(location, occupier, occupyMask))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OnOccupierAdded(new OccupierAddedEventArgs<T>(metrics, location, occupier));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Add(int x, int y, T occupier, bool[,] occupyMask) => Add(new Point(x, y), occupier, occupyMask);
|
||||
|
||||
public bool Add(int cell, T occupier, bool[,] occupyMask) => metrics.GetLocation(cell, out Point location) ? Add(location, occupier, occupyMask) : false;
|
||||
|
||||
public bool Add(Point location, T occupier) => (occupier != null) ? Add(location, occupier, occupier.OccupyMask) : false;
|
||||
|
||||
public bool Add(int x, int y, T occupier) => (occupier != null) ? Add(x, y, occupier, occupier.OccupyMask) : false;
|
||||
|
||||
public bool Add(int cell, T occupier) => (occupier != null) ? Add(cell, occupier, occupier.OccupyMask) : false;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
occupiers.Clear();
|
||||
Array.Clear(occupierCells, 0, occupierCells.Length);
|
||||
OnCleared();
|
||||
}
|
||||
|
||||
public bool Contains(int x, int y) => ((x >= 0) && (x < occupierCells.GetLength(1)) && (y >= 0) && (y < occupierCells.GetLength(0)));
|
||||
|
||||
public bool Contains(Point location) => Contains(location.X, location.Y);
|
||||
|
||||
public bool Contains(int cell) => metrics.GetLocation(cell, out Point location) ? Contains(location) : false;
|
||||
|
||||
public bool Contains(T occupier) => occupiers.ContainsKey(occupier);
|
||||
|
||||
public IEnumerator<(Point Location, T Occupier)> GetEnumerator() => occupiers.Select(kv => (kv.Value, kv.Key)).GetEnumerator();
|
||||
|
||||
public bool Remove(T occupier)
|
||||
{
|
||||
var oldLocation = this[occupier];
|
||||
if (!DoRemove(occupier))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OnOccupierRemoved(new OccupierRemovedEventArgs<T>(metrics, oldLocation.Value, occupier));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Remove(Point location) => Remove(this[location]);
|
||||
|
||||
public bool Remove(int x, int y) => Remove(new Point(x, y));
|
||||
|
||||
public bool Remove(int cell) => metrics.GetLocation(cell, out Point location) ? Remove(location) : false;
|
||||
|
||||
public IEnumerable<(Point Location, U Occupier)> OfType<U>() where U : T => this.Where(i => i.Occupier is U).Select(i => (i.Location, (U)i.Occupier));
|
||||
|
||||
protected virtual void OnOccupierAdded(OccupierAddedEventArgs<T> e)
|
||||
{
|
||||
OccupierAdded?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnOccupierRemoved(OccupierRemovedEventArgs<T> e)
|
||||
{
|
||||
OccupierRemoved?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnCleared()
|
||||
{
|
||||
Cleared?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
private bool DoAdd(Point location, T occupier, bool[,] occupyMask)
|
||||
{
|
||||
if ((occupier == null) || Contains(occupier))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var occupyPoints = GetOccupyPoints(location, occupyMask).ToArray();
|
||||
if (occupyPoints.Any(p => !Contains(p) || (this[p] != null)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
occupiers[occupier] = location;
|
||||
foreach (var p in occupyPoints)
|
||||
{
|
||||
occupierCells[p.Y, p.X] = occupier;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool DoRemove(T occupier)
|
||||
{
|
||||
if ((occupier == null) || !occupiers.TryGetValue(occupier, out Point location))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
occupiers.Remove(occupier);
|
||||
for (var y = location.Y; y < metrics.Height; ++y)
|
||||
{
|
||||
for (var x = location.X; x < metrics.Width; ++x)
|
||||
{
|
||||
if (occupierCells[y, x] == occupier)
|
||||
{
|
||||
occupierCells[y, x] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static IEnumerable<Point> GetOccupyPoints(Point location, bool[,] occupyMask)
|
||||
{
|
||||
for (var y = 0; y < occupyMask.GetLength(0); ++y)
|
||||
{
|
||||
for (var x = 0; x < occupyMask.GetLength(1); ++x)
|
||||
{
|
||||
if (occupyMask[y, x])
|
||||
{
|
||||
yield return location + new Size(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Point> GetOccupyPoints(Point location, T occupier) => GetOccupyPoints(location, occupier.OccupyMask);
|
||||
}
|
||||
}
|
107
CnCTDRAMapEditor/Model/OverlapperSet.cs
Normal file
107
CnCTDRAMapEditor/Model/OverlapperSet.cs
Normal file
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// 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.Utility;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class OverlapperSet<T> : IEnumerable<(Point Location, T Overlapper)>, IEnumerable where T : class, ICellOverlapper
|
||||
{
|
||||
private readonly CellMetrics metrics;
|
||||
private readonly Dictionary<T, Rectangle> overlappers = new Dictionary<T, Rectangle>();
|
||||
|
||||
public Rectangle? this[T overlapper] => Contains(overlapper) ? overlappers[overlapper] : default;
|
||||
|
||||
public IEnumerable<T> Overlappers => overlappers.Keys;
|
||||
|
||||
public OverlapperSet(CellMetrics metrics)
|
||||
{
|
||||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
public bool Add(Point location, T overlapper)
|
||||
{
|
||||
if ((overlapper == null) || Contains(overlapper))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var rectangle = overlapper.OverlapBounds;
|
||||
rectangle.Offset(location);
|
||||
overlappers[overlapper] = rectangle;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Add(int x, int y, T occupier) => Add(new Point(x, y), occupier);
|
||||
|
||||
public bool Add(int cell, T overlapper) => metrics.GetLocation(cell, out Point location) ? Add(location, overlapper) : false;
|
||||
|
||||
public void Clear() => overlappers.Clear();
|
||||
|
||||
public bool Contains(T occupier) => overlappers.ContainsKey(occupier);
|
||||
|
||||
public void CopyTo(OverlapperSet<T> other)
|
||||
{
|
||||
foreach (var (Location, Occupier) in this)
|
||||
{
|
||||
other.Add(Location, Occupier);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(Point Location, T Overlapper)> GetEnumerator() => overlappers.Select(kv => (kv.Value.Location, kv.Key)).GetEnumerator();
|
||||
|
||||
public bool Remove(T overlapper)
|
||||
{
|
||||
if ((overlapper == null) || !overlappers.TryGetValue(overlapper, out Rectangle overlapRect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
overlappers.Remove(overlapper);
|
||||
return true;
|
||||
}
|
||||
|
||||
public ISet<Point> Overlaps(IEnumerable<Rectangle> rectangles)
|
||||
{
|
||||
var rectangleSet = new HashSet<Rectangle>(rectangles);
|
||||
while (true)
|
||||
{
|
||||
var count = rectangleSet.Count;
|
||||
var overlap = overlappers.Values.Where(x => rectangleSet.Any(y => x.IntersectsWith(y))).ToArray();
|
||||
rectangleSet.UnionWith(overlap);
|
||||
if (rectangleSet.Count == count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rectangleSet.SelectMany(x => x.Points()).ToHashSet();
|
||||
}
|
||||
|
||||
public ISet<Point> Overlaps(Rectangle rectangle) => Overlaps(rectangle.Yield());
|
||||
|
||||
public ISet<Point> Overlaps(IEnumerable<Point> points) => Overlaps(points.Select(p => new Rectangle(p, new Size(1, 1))));
|
||||
|
||||
public ISet<Point> Overlaps(Point point) => Overlaps(point.Yield());
|
||||
|
||||
public IEnumerable<(Point Location, U Overlapper)> OfType<U>() where U : T => this.Where(i => i.Overlapper is U).Select(i => (i.Location, (U)i.Overlapper));
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
}
|
32
CnCTDRAMapEditor/Model/Overlay.cs
Normal file
32
CnCTDRAMapEditor/Model/Overlay.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// 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 System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class Overlay : ICellOccupier
|
||||
{
|
||||
public OverlayType Type { get; set; }
|
||||
|
||||
public int Icon { get; set; }
|
||||
|
||||
public Size OverlapSize => new Size(1, 1);
|
||||
|
||||
public bool[,] OccupyMask => Type.OccupyMask;
|
||||
|
||||
public Color Tint { get; set; } = Color.White;
|
||||
}
|
||||
}
|
128
CnCTDRAMapEditor/Model/OverlayType.cs
Normal file
128
CnCTDRAMapEditor/Model/OverlayType.cs
Normal file
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// 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.Utility;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
[Flags]
|
||||
public enum OverlayTypeFlag
|
||||
{
|
||||
None = 0,
|
||||
TiberiumOrGold = (1 << 0),
|
||||
Gems = (1 << 1),
|
||||
Wall = (1 << 2),
|
||||
Crate = (1 << 3),
|
||||
Flag = (1 << 4),
|
||||
}
|
||||
|
||||
public class OverlayType : ICellOccupier, IBrowsableType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
public TheaterType[] Theaters { get; private set; }
|
||||
|
||||
public OverlayTypeFlag Flag { get; private set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public bool[,] OccupyMask => new bool[1, 1] { { true } };
|
||||
|
||||
public bool IsResource => (Flag & (OverlayTypeFlag.TiberiumOrGold | OverlayTypeFlag.Gems)) != OverlayTypeFlag.None;
|
||||
|
||||
public bool IsTiberiumOrGold => (Flag & OverlayTypeFlag.TiberiumOrGold) != OverlayTypeFlag.None;
|
||||
|
||||
public bool IsGem => (Flag & OverlayTypeFlag.Gems) != OverlayTypeFlag.None;
|
||||
|
||||
public bool IsWall => (Flag & OverlayTypeFlag.Wall) != OverlayTypeFlag.None;
|
||||
|
||||
public bool IsCrate => (Flag & OverlayTypeFlag.Crate) != OverlayTypeFlag.None;
|
||||
|
||||
public bool IsFlag => (Flag & OverlayTypeFlag.Flag) != OverlayTypeFlag.None;
|
||||
|
||||
public bool IsPlaceable => (Flag & ~OverlayTypeFlag.Crate) == OverlayTypeFlag.None;
|
||||
|
||||
public OverlayType(sbyte id, string name, string textId, TheaterType[] theaters, OverlayTypeFlag flag)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
DisplayName = Globals.TheGameTextManager[textId];
|
||||
Theaters = theaters;
|
||||
Flag = flag;
|
||||
}
|
||||
|
||||
public OverlayType(sbyte id, string name, string textId, OverlayTypeFlag flag)
|
||||
: this(id, name, textId, null, flag)
|
||||
{
|
||||
}
|
||||
|
||||
public OverlayType(sbyte id, string name, string textId, TheaterType[] theaters)
|
||||
: this(id, name, textId, theaters, OverlayTypeFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public OverlayType(sbyte id, string name, OverlayTypeFlag flag)
|
||||
: this(id, name, name, null, flag)
|
||||
{
|
||||
}
|
||||
|
||||
public OverlayType(sbyte id, string name, string textId)
|
||||
: this(id, name, textId, null, OverlayTypeFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is OverlayType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(TheaterType theater)
|
||||
{
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, 0, out Tile tile))
|
||||
{
|
||||
Thumbnail = new Bitmap(tile.Image, tile.Image.Width, tile.Image.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
CnCTDRAMapEditor/Model/Smudge.cs
Normal file
30
CnCTDRAMapEditor/Model/Smudge.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// 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 System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class Smudge
|
||||
{
|
||||
public SmudgeType Type { get; set; }
|
||||
|
||||
public int Icon { get; set; }
|
||||
|
||||
public int Data { get; set; }
|
||||
|
||||
public Color Tint { get; set; } = Color.White;
|
||||
}
|
||||
}
|
116
CnCTDRAMapEditor/Model/SmudgeType.cs
Normal file
116
CnCTDRAMapEditor/Model/SmudgeType.cs
Normal file
|
@ -0,0 +1,116 @@
|
|||
//
|
||||
// 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.Utility;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
[Flags]
|
||||
public enum SmudgeTypeFlag
|
||||
{
|
||||
None = 0,
|
||||
Bib = 1,
|
||||
Bib1 = 3,
|
||||
Bib2 = 5,
|
||||
Bib3 = 9,
|
||||
}
|
||||
|
||||
public class SmudgeType : IBrowsableType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string DisplayName => Name;
|
||||
|
||||
public Size Size { get; set; }
|
||||
|
||||
public SmudgeTypeFlag Flag { get; private set; }
|
||||
|
||||
public Size RenderSize { get; set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public SmudgeType(sbyte id, string name, Size size, SmudgeTypeFlag flag)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
Size = size;
|
||||
Flag = flag;
|
||||
}
|
||||
|
||||
public SmudgeType(sbyte id, string name, Size size)
|
||||
: this(id, name, size, SmudgeTypeFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public SmudgeType(sbyte id, string name)
|
||||
: this(id, name, new Size(1, 1), SmudgeTypeFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is SmudgeType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(TheaterType theater)
|
||||
{
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, 0, out Tile tile))
|
||||
{
|
||||
if ((tile.Image.Width * Globals.TileHeight) > (tile.Image.Height * Globals.TileWidth))
|
||||
{
|
||||
RenderSize = new Size(
|
||||
tile.Image.Width * Globals.TileWidth / tile.Image.Width,
|
||||
tile.Image.Height * Globals.TileWidth / tile.Image.Width
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderSize = new Size(
|
||||
tile.Image.Width * Globals.TileHeight / tile.Image.Height,
|
||||
tile.Image.Height * Globals.TileHeight / tile.Image.Height
|
||||
);
|
||||
}
|
||||
Thumbnail = new Bitmap(tile.Image, tile.Image.Width, tile.Image.Height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
CnCTDRAMapEditor/Model/SteamSection.cs
Normal file
37
CnCTDRAMapEditor/Model/SteamSection.cs
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// 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 Steamworks;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class SteamSection
|
||||
{
|
||||
[DefaultValue(null)]
|
||||
public string Title { get; set; }
|
||||
|
||||
[DefaultValue(null)]
|
||||
public string Description { get; set; }
|
||||
|
||||
[DefaultValue(null)]
|
||||
public string PreviewFile { get; set; }
|
||||
|
||||
[DefaultValue(ERemoteStoragePublishedFileVisibility.k_ERemoteStoragePublishedFileVisibilityPublic)]
|
||||
public ERemoteStoragePublishedFileVisibility Visibility { get; set; }
|
||||
|
||||
[DefaultValue(typeof(ulong), "0")]
|
||||
public ulong PublishedFileId { get; set; }
|
||||
}
|
||||
}
|
158
CnCTDRAMapEditor/Model/TeamType.cs
Normal file
158
CnCTDRAMapEditor/Model/TeamType.cs
Normal file
|
@ -0,0 +1,158 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class TeamTypeClass : ICloneable
|
||||
{
|
||||
public ITechnoType Type { get; set; }
|
||||
|
||||
public byte Count { get; set; }
|
||||
|
||||
public TeamTypeClass Clone()
|
||||
{
|
||||
return new TeamTypeClass()
|
||||
{
|
||||
Type = Type,
|
||||
Count = Count
|
||||
};
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public class TeamTypeMission : ICloneable
|
||||
{
|
||||
public string Mission { get; set; }
|
||||
|
||||
public int Argument { get; set; }
|
||||
|
||||
public TeamTypeMission Clone()
|
||||
{
|
||||
return new TeamTypeMission()
|
||||
{
|
||||
Mission = Mission,
|
||||
Argument = Argument
|
||||
};
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public class TeamType : INamedType, ICloneable
|
||||
{
|
||||
public static readonly string None = "None";
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public HouseType House { get; set; }
|
||||
|
||||
public bool IsRoundAbout { get; set; }
|
||||
|
||||
public bool IsLearning { get; set; }
|
||||
|
||||
public bool IsSuicide { get; set; }
|
||||
|
||||
public bool IsAutocreate { get; set; }
|
||||
|
||||
public bool IsMercenary { get; set; }
|
||||
|
||||
public int RecruitPriority { get; set; }
|
||||
|
||||
public byte MaxAllowed { get; set; }
|
||||
|
||||
public byte InitNum { get; set; }
|
||||
|
||||
public byte Fear { get; set; }
|
||||
|
||||
public bool IsReinforcable { get; set; }
|
||||
|
||||
public bool IsPrebuilt { get; set; }
|
||||
|
||||
public int Origin { get; set; }
|
||||
|
||||
public string Trigger { get; set; } = Model.Trigger.None;
|
||||
|
||||
public List<TeamTypeClass> Classes { get; } = new List<TeamTypeClass>();
|
||||
|
||||
public List<TeamTypeMission> Missions { get; } = new List<TeamTypeMission>();
|
||||
|
||||
public TeamType Clone()
|
||||
{
|
||||
var teamType = new TeamType()
|
||||
{
|
||||
Name = Name,
|
||||
House = House,
|
||||
IsRoundAbout = IsRoundAbout,
|
||||
IsLearning = IsLearning,
|
||||
IsSuicide = IsSuicide,
|
||||
IsAutocreate = IsAutocreate,
|
||||
IsMercenary = IsMercenary,
|
||||
RecruitPriority = RecruitPriority,
|
||||
MaxAllowed = MaxAllowed,
|
||||
InitNum = InitNum,
|
||||
Fear = Fear,
|
||||
IsReinforcable = IsReinforcable,
|
||||
IsPrebuilt = IsPrebuilt,
|
||||
Origin = Origin,
|
||||
Trigger = Trigger
|
||||
};
|
||||
|
||||
teamType.Classes.AddRange(Classes.Select(c => c.Clone()));
|
||||
teamType.Missions.AddRange(Missions.Select(m => m.Clone()));
|
||||
|
||||
return teamType;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is TeamType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
}
|
30
CnCTDRAMapEditor/Model/Template.cs
Normal file
30
CnCTDRAMapEditor/Model/Template.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// 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 System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class Template : ICellOccupier
|
||||
{
|
||||
public TemplateType Type { get; set; }
|
||||
|
||||
public int Icon { get; set; }
|
||||
|
||||
public Size OverlapSize => new Size(1, 1);
|
||||
|
||||
public bool[,] OccupyMask => new bool[1, 1] { { true } };
|
||||
}
|
||||
}
|
133
CnCTDRAMapEditor/Model/TemplateType.cs
Normal file
133
CnCTDRAMapEditor/Model/TemplateType.cs
Normal file
|
@ -0,0 +1,133 @@
|
|||
//
|
||||
// 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.Utility;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
[Flags]
|
||||
public enum TemplateTypeFlag
|
||||
{
|
||||
None = 0,
|
||||
Clear = (1 << 1),
|
||||
Water = (1 << 2),
|
||||
OreMine = (1 << 3),
|
||||
}
|
||||
|
||||
public class TemplateType : IBrowsableType
|
||||
{
|
||||
public ushort ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string DisplayName => Name;
|
||||
|
||||
public int IconWidth { get; private set; }
|
||||
|
||||
public int IconHeight { get; private set; }
|
||||
|
||||
public Size IconSize => new Size(IconWidth, IconHeight);
|
||||
|
||||
public int NumIcons => IconWidth * IconHeight;
|
||||
|
||||
public bool[,] IconMask { get; set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public TheaterType[] Theaters { get; private set; }
|
||||
|
||||
public TemplateTypeFlag Flag { get; private set; }
|
||||
|
||||
public TemplateType(ushort id, string name, int iconWidth, int iconHeight, TheaterType[] theaters, TemplateTypeFlag flag)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
IconWidth = iconWidth;
|
||||
IconHeight = iconHeight;
|
||||
Theaters = theaters;
|
||||
Flag = flag;
|
||||
}
|
||||
|
||||
public TemplateType(ushort id, string name, int iconWidth, int iconHeight, TheaterType[] theaters)
|
||||
: this(id, name, iconWidth, iconHeight, theaters, TemplateTypeFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is TemplateType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is byte)
|
||||
{
|
||||
return ID == (byte)obj;
|
||||
}
|
||||
else if (obj is ushort)
|
||||
{
|
||||
return ID == (ushort)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(TheaterType theater)
|
||||
{
|
||||
var size = new Size(Globals.OriginalTileWidth / 4, Globals.OriginalTileWidth / 4);
|
||||
var iconSize = Math.Max(IconWidth, IconHeight);
|
||||
var thumbnail = new Bitmap(iconSize * size.Width, iconSize * size.Height);
|
||||
var mask = new bool[IconWidth, IconHeight];
|
||||
Array.Clear(mask, 0, mask.Length);
|
||||
|
||||
bool found = false;
|
||||
using (var g = Graphics.FromImage(thumbnail))
|
||||
{
|
||||
g.Clear(Color.Transparent);
|
||||
|
||||
int icon = 0;
|
||||
for (var y = 0; y < IconHeight; ++y)
|
||||
{
|
||||
for (var x = 0; x < IconWidth; ++x, ++icon)
|
||||
{
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, icon, out Tile tile))
|
||||
{
|
||||
g.DrawImage(tile.Image, x * size.Width, y * size.Height, size.Width, size.Height);
|
||||
found = mask[x, y] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Thumbnail = found ? thumbnail : null;
|
||||
IconMask = mask;
|
||||
}
|
||||
}
|
||||
}
|
71
CnCTDRAMapEditor/Model/Terrain.cs
Normal file
71
CnCTDRAMapEditor/Model/Terrain.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
using MobiusEditor.Interface;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class Terrain : ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private TerrainType type;
|
||||
public TerrainType Type { get => type; set => SetField(ref type, value); }
|
||||
|
||||
private int icon;
|
||||
public int Icon { get => icon; set => SetField(ref icon, value); }
|
||||
|
||||
public Rectangle OverlapBounds => Type.OverlapBounds;
|
||||
|
||||
public bool[,] OccupyMask => Type.OccupyMask;
|
||||
|
||||
private string trigger = Model.Trigger.None;
|
||||
public string Trigger { get => trigger; set => SetField(ref trigger, value); }
|
||||
|
||||
public Color Tint { get; set; } = Color.White;
|
||||
|
||||
public Terrain Clone()
|
||||
{
|
||||
return new Terrain()
|
||||
{
|
||||
Type = Type,
|
||||
Icon = Icon,
|
||||
Trigger = Trigger
|
||||
};
|
||||
}
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
}
|
117
CnCTDRAMapEditor/Model/TerrainType.cs
Normal file
117
CnCTDRAMapEditor/Model/TerrainType.cs
Normal file
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// 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.Utility;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class TerrainType : ICellOverlapper, ICellOccupier, IBrowsableType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string DisplayName => Name;
|
||||
|
||||
public Rectangle OverlapBounds => new Rectangle(
|
||||
Point.Empty,
|
||||
new Size(((RenderSize.Width + Globals.TileWidth - 1) / Globals.TileWidth), ((RenderSize.Height + Globals.TileHeight - 1) / Globals.TileHeight))
|
||||
);
|
||||
|
||||
public bool[,] OccupyMask { get; private set; }
|
||||
|
||||
public Size Size => new Size(OccupyMask.GetLength(1), OccupyMask.GetLength(0));
|
||||
|
||||
public TheaterType[] Theaters { get; private set; }
|
||||
|
||||
public bool IsTransformable { get; private set; }
|
||||
|
||||
public TemplateTypeFlag TemplateType { get; private set; }
|
||||
|
||||
public Size RenderSize { get; set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public TerrainType(sbyte id, string name, TheaterType[] theaters, bool[,] occupyMask, bool isTransformable, TemplateTypeFlag templateType)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
Theaters = theaters;
|
||||
OccupyMask = occupyMask;
|
||||
IsTransformable = isTransformable;
|
||||
TemplateType = templateType;
|
||||
}
|
||||
|
||||
public TerrainType(sbyte id, string name, TheaterType[] theaters, bool[,] occupyMask, bool isTransformable)
|
||||
: this(id, name, theaters, occupyMask, isTransformable, TemplateTypeFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public TerrainType(sbyte id, string name, TheaterType[] theaters, bool[,] occupyMask, TemplateTypeFlag templateType)
|
||||
: this(id, name, theaters, occupyMask, false, templateType)
|
||||
{
|
||||
}
|
||||
|
||||
public TerrainType(sbyte id, string name, TheaterType[] theaters, bool[,] occupyMask)
|
||||
: this(id, name, theaters, occupyMask, false, TemplateTypeFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is TerrainType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(TheaterType theater)
|
||||
{
|
||||
string tileName = Name;
|
||||
if ((TemplateType & TemplateTypeFlag.OreMine) != TemplateTypeFlag.None)
|
||||
{
|
||||
tileName = "OREMINE";
|
||||
}
|
||||
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, tileName, IsTransformable ? 22 : 0, out Tile tile))
|
||||
{
|
||||
RenderSize = new Size(tile.Image.Width / Globals.TileScale, tile.Image.Height / Globals.TileScale);
|
||||
Thumbnail = new Bitmap(tile.Image, tile.Image.Width / 2, tile.Image.Height / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
CnCTDRAMapEditor/Model/TheaterType.cs
Normal file
63
CnCTDRAMapEditor/Model/TheaterType.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class TheaterType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public IEnumerable<string> Tilesets { get; private set; }
|
||||
|
||||
public TheaterType(sbyte id, string name, IEnumerable<string> tilesets)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
Tilesets = tilesets;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is TheaterType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
154
CnCTDRAMapEditor/Model/Trigger.cs
Normal file
154
CnCTDRAMapEditor/Model/Trigger.cs
Normal file
|
@ -0,0 +1,154 @@
|
|||
//
|
||||
// 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 System;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public enum TriggerPersistantType
|
||||
{
|
||||
Volatile = 0,
|
||||
SemiPersistant = 1,
|
||||
Persistant = 2
|
||||
}
|
||||
|
||||
public enum TriggerMultiStyleType
|
||||
{
|
||||
Only = 0,
|
||||
And = 1,
|
||||
Or = 2,
|
||||
Linked = 3
|
||||
}
|
||||
|
||||
public class TriggerEvent : ICloneable
|
||||
{
|
||||
public static readonly string None = "None";
|
||||
|
||||
public string EventType { get; set; }
|
||||
|
||||
public string Team { get; set; }
|
||||
|
||||
public long Data { get; set; }
|
||||
|
||||
public TriggerEvent Clone()
|
||||
{
|
||||
return new TriggerEvent()
|
||||
{
|
||||
EventType = EventType,
|
||||
Team = Team,
|
||||
Data = Data
|
||||
};
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public class TriggerAction : ICloneable
|
||||
{
|
||||
public static readonly string None = "None";
|
||||
|
||||
public string ActionType { get; set; }
|
||||
|
||||
public string Trigger { get; set; }
|
||||
|
||||
public string Team { get; set; }
|
||||
|
||||
public long Data { get; set; }
|
||||
|
||||
public TriggerAction Clone()
|
||||
{
|
||||
return new TriggerAction()
|
||||
{
|
||||
ActionType = ActionType,
|
||||
Trigger = Trigger,
|
||||
Team = Team,
|
||||
Data = Data
|
||||
};
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public class Trigger : INamedType, ICloneable
|
||||
{
|
||||
public static readonly string None = "None";
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public TriggerPersistantType PersistantType { get; set; } = TriggerPersistantType.Volatile;
|
||||
|
||||
public string House { get; set; }
|
||||
|
||||
public TriggerMultiStyleType EventControl { get; set; } = TriggerMultiStyleType.Only;
|
||||
|
||||
public TriggerEvent Event1 { get; private set; } = new TriggerEvent { EventType = TriggerEvent.None };
|
||||
|
||||
public TriggerEvent Event2 { get; private set; } = new TriggerEvent { EventType = TriggerEvent.None };
|
||||
|
||||
public TriggerAction Action1 { get; private set; } = new TriggerAction { ActionType = TriggerEvent.None };
|
||||
|
||||
public TriggerAction Action2 { get; private set; } = new TriggerAction { ActionType = TriggerEvent.None };
|
||||
|
||||
public Trigger Clone()
|
||||
{
|
||||
return new Trigger()
|
||||
{
|
||||
Name = Name,
|
||||
PersistantType = PersistantType,
|
||||
House = House,
|
||||
EventControl = EventControl,
|
||||
Event1 = Event1.Clone(),
|
||||
Event2 = Event2.Clone(),
|
||||
Action1 = Action1.Clone(),
|
||||
Action2 = Action2.Clone()
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Trigger)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
}
|
29
CnCTDRAMapEditor/Model/TypeItem.cs
Normal file
29
CnCTDRAMapEditor/Model/TypeItem.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// 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
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class TypeItem<T>
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public T Type { get; private set; }
|
||||
|
||||
public TypeItem(string name, T type)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
}
|
83
CnCTDRAMapEditor/Model/Unit.cs
Normal file
83
CnCTDRAMapEditor/Model/Unit.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// 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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public class Unit : ICellOverlapper, ICellOccupier, INotifyPropertyChanged, ICloneable
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private UnitType type;
|
||||
public UnitType Type { get => type; set => SetField(ref type, value); }
|
||||
|
||||
public Rectangle OverlapBounds => Type.OverlapBounds;
|
||||
|
||||
public bool[,] OccupyMask => Type.OccupyMask;
|
||||
|
||||
private HouseType house;
|
||||
public HouseType House { get => house; set => SetField(ref house, value); }
|
||||
|
||||
private int strength;
|
||||
public int Strength { get => strength; set => SetField(ref strength, value); }
|
||||
|
||||
private DirectionType direction;
|
||||
public DirectionType Direction { get => direction; set => SetField(ref direction, value); }
|
||||
|
||||
private string mission;
|
||||
public string Mission { get => mission; set => SetField(ref mission, value); }
|
||||
|
||||
private string trigger = Model.Trigger.None;
|
||||
public string Trigger { get => trigger; set => SetField(ref trigger, value); }
|
||||
|
||||
public Color Tint { get; set; } = Color.White;
|
||||
|
||||
public Unit Clone()
|
||||
{
|
||||
return new Unit()
|
||||
{
|
||||
Type = Type,
|
||||
House = House,
|
||||
Strength = Strength,
|
||||
Direction = Direction,
|
||||
Mission = Mission,
|
||||
Trigger = Trigger
|
||||
};
|
||||
}
|
||||
|
||||
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(field, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
field = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
object ICloneable.Clone()
|
||||
{
|
||||
return Clone();
|
||||
}
|
||||
}
|
||||
}
|
137
CnCTDRAMapEditor/Model/UnitType.cs
Normal file
137
CnCTDRAMapEditor/Model/UnitType.cs
Normal file
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// 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.Drawing;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public static class UnitTypeIDMask
|
||||
{
|
||||
public const sbyte Aircraft = 1 << 5;
|
||||
public const sbyte Vessel = 1 << 6;
|
||||
}
|
||||
|
||||
public class UnitType : ICellOverlapper, ICellOccupier, ITechnoType, IBrowsableType
|
||||
{
|
||||
public sbyte ID { get; private set; }
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string DisplayName { get; private set; }
|
||||
|
||||
public Rectangle OverlapBounds => new Rectangle(-1, -1, 3, 3);
|
||||
|
||||
public bool[,] OccupyMask => new bool[1, 1] { { true } };
|
||||
|
||||
public string OwnerHouse { get; private set; }
|
||||
|
||||
public bool HasTurret { get; private set; }
|
||||
|
||||
public bool IsFixedWing { get; private set; }
|
||||
|
||||
public bool IsUnit => !IsAircraft && !IsVessel;
|
||||
|
||||
public bool IsAircraft => (ID & UnitTypeIDMask.Aircraft) != 0;
|
||||
|
||||
public bool IsVessel => (ID & UnitTypeIDMask.Vessel) != 0;
|
||||
|
||||
public Size RenderSize { get; set; }
|
||||
|
||||
public Image Thumbnail { get; set; }
|
||||
|
||||
public UnitType(sbyte id, string name, string textId, string ownerHouse, bool hasTurret, bool isFixedWing)
|
||||
{
|
||||
ID = id;
|
||||
Name = name;
|
||||
DisplayName = Globals.TheGameTextManager[textId];
|
||||
OwnerHouse = ownerHouse;
|
||||
HasTurret = hasTurret;
|
||||
IsFixedWing = isFixedWing;
|
||||
}
|
||||
|
||||
public UnitType(sbyte id, string name, string textId, string ownerHouse, bool hasTurret)
|
||||
: this(id, name, textId, ownerHouse, hasTurret, false)
|
||||
{
|
||||
}
|
||||
|
||||
public UnitType(sbyte id, string name, string textId)
|
||||
: this(id, name, textId, null, false)
|
||||
{
|
||||
}
|
||||
|
||||
public UnitType(sbyte id, string name, string textId, string ownerHouse)
|
||||
: this(id, name, textId, ownerHouse, false)
|
||||
{
|
||||
}
|
||||
|
||||
public UnitType(sbyte id, string name, string textId, bool hasTurret)
|
||||
: this(id, name, textId, null, hasTurret)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is UnitType)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is sbyte)
|
||||
{
|
||||
return ID == (sbyte)obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ID.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
public void Init(GameType gameType, TheaterType theater, HouseType house, DirectionType direction)
|
||||
{
|
||||
if (Globals.TheTilesetManager.GetTileData(theater.Tilesets, Name, 0, out Tile tile))
|
||||
{
|
||||
RenderSize = new Size(tile.Image.Width / Globals.TileScale, tile.Image.Height / Globals.TileScale);
|
||||
}
|
||||
|
||||
var mockUnit = new Unit()
|
||||
{
|
||||
Type = this,
|
||||
House = house,
|
||||
Strength = 256,
|
||||
Direction = direction
|
||||
};
|
||||
var unitThumbnail = new Bitmap(Globals.TileWidth * 3, Globals.TileHeight * 3);
|
||||
using (var g = Graphics.FromImage(unitThumbnail))
|
||||
{
|
||||
MapRenderer.Render(gameType, theater, new Point(1, 1), Globals.TileSize, mockUnit).Item2(g);
|
||||
}
|
||||
Thumbnail = unitThumbnail;
|
||||
}
|
||||
}
|
||||
}
|
69
CnCTDRAMapEditor/Model/Waypoint.cs
Normal file
69
CnCTDRAMapEditor/Model/Waypoint.cs
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// 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 System;
|
||||
|
||||
namespace MobiusEditor.Model
|
||||
{
|
||||
public enum WaypointFlag
|
||||
{
|
||||
None,
|
||||
PlayerStart
|
||||
}
|
||||
|
||||
public class Waypoint : INamedType
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public WaypointFlag Flag { get; private set; }
|
||||
|
||||
public int? Cell { get; set; }
|
||||
|
||||
public Waypoint(string name, WaypointFlag flag)
|
||||
{
|
||||
Name = name;
|
||||
Flag = flag;
|
||||
}
|
||||
|
||||
public Waypoint(string name)
|
||||
: this(name, WaypointFlag.None)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Waypoint)
|
||||
{
|
||||
return this == obj;
|
||||
}
|
||||
else if (obj is string)
|
||||
{
|
||||
return string.Equals(Name, obj as string, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue