diff --git a/FrEee.Tests/FrEee.Tests.csproj b/FrEee.Tests/FrEee.Tests.csproj
index fec84945..ab19b8c8 100644
--- a/FrEee.Tests/FrEee.Tests.csproj
+++ b/FrEee.Tests/FrEee.Tests.csproj
@@ -13,9 +13,9 @@
-
-
-
+
+
+
diff --git a/FrEee.Tests/Utility/VectorTest.cs b/FrEee.Tests/Utility/VectorTest.cs
index 8d302bf8..2d869579 100644
--- a/FrEee.Tests/Utility/VectorTest.cs
+++ b/FrEee.Tests/Utility/VectorTest.cs
@@ -6,22 +6,22 @@ namespace FrEee.Tests.Utility;
public class VectorTest
{
[Test]
- public void IntVector2AddLinearGradientEightWay()
+ public void AddLinearGradientEightWay()
{
var map = new HeatMap();
- map.AddLinearGradientEightWay(new IntVector2(0, 0), 10, 10, -1);
- map.AddLinearGradientEightWay(new IntVector2(1, 1), 2, 2, -1);
+ map.AddLinearGradientEightWay(new Vector2(0, 0), 10, 10, -1);
+ map.AddLinearGradientEightWay(new Vector2(1, 1), 2, 2, -1);
Assert.AreEqual(11, map[0, 0]);
Assert.AreEqual(11, map[1, 1]);
Assert.AreEqual(0, map[99, 99]);
}
[Test]
- public void IntVector2InterpolationEightWay()
+ public void InterpolationEightWay()
{
- var v1 = new IntVector2(-1, 3);
- var v2 = new IntVector2(6, 4);
- var interp = IntVector2.InterpolateEightWay(v1, v2, 3);
+ var v1 = new Vector2(-1, 3);
+ var v2 = new Vector2(6, 4);
+ var interp = Vector2.InterpolateEightWay(v1, v2, 3);
var trip = v2 - v1;
var traveled = interp - v1;
var togo = v2 - interp;
diff --git a/FrEee.WinForms/Controls/BattleView.cs b/FrEee.WinForms/Controls/BattleView.cs
index c32d5fdd..08297728 100644
--- a/FrEee.WinForms/Controls/BattleView.cs
+++ b/FrEee.WinForms/Controls/BattleView.cs
@@ -51,12 +51,12 @@ public Battle Battle
}
}
- public IntVector2 ClickLocation { get; private set; }
+ public Vector2 ClickLocation { get; private set; }
///
/// The combat sector which is focused.
///
- public IntVector2 FocusedLocation
+ public Vector2 FocusedLocation
{
get => focusedLocation;
set
@@ -138,8 +138,8 @@ public bool UseSquares
private Battle battle;
private List booms = new List();
private bool combatPhase = false;
- private IntVector2 focusedLocation;
- private SafeDictionary locations = new SafeDictionary();
+ private Vector2 focusedLocation;
+ private SafeDictionary> locations = [];
private List pewpews = new List();
///
@@ -201,7 +201,7 @@ protected override void OnPaint(PaintEventArgs pe)
var drawx = drawPoint.X;
var drawy = drawPoint.Y;
- var pos = new IntVector2(x, y);
+ var pos = new Vector2(x, y);
// draw image, owner flag, and name of largest space object (if any)
var here = Battle.Combatants.Where(q => locations.Any(w => (w.Key == q || w.Key == Battle.StartCombatants[q.ID]) && w.Value == pos));
@@ -331,13 +331,13 @@ private void BattleView_SizeChanged(object sender, EventArgs e)
Invalidate();
}
- private IntVector2 GetClickPoint(int x, int y)
+ private Vector2 GetClickPoint(int x, int y)
{
if (AutoZoom)
{
var clickx = (x - SectorBorderSize) / (SectorDrawSize + SectorBorderSize) + Battle.UpperLeft[round].X;
var clicky = (y - SectorBorderSize) / (SectorDrawSize + SectorBorderSize) + Battle.UpperLeft[round].Y;
- return new IntVector2(clickx, clicky);
+ return new Vector2(clickx, clicky);
}
else
{
@@ -345,7 +345,7 @@ private IntVector2 GetClickPoint(int x, int y)
FocusedLocation = (Battle.LowerRight[round] - Battle.UpperLeft[round]) / 2;
var clickx = (x - Width / 2 - SectorBorderSize) / (SectorDrawSize + SectorBorderSize) + FocusedLocation.X;
var clicky = (y - Height / 2 - SectorBorderSize) / (SectorDrawSize + SectorBorderSize) + FocusedLocation.Y;
- return new IntVector2(clickx, clicky);
+ return new Vector2(clickx, clicky);
}
}
@@ -463,28 +463,28 @@ private void UpdateData()
private class Boom
{
- public Boom(IntVector2 pos, float size)
+ public Boom(Vector2 pos, float size)
{
Position = pos;
Size = size;
}
- public IntVector2 Position { get; set; }
+ public Vector2 Position { get; set; }
public float Size { get; set; }
}
private class Pewpew
{
- public Pewpew(IntVector2 start, IntVector2 end, bool isHit = true)
+ public Pewpew(Vector2 start, Vector2 end, bool isHit = true)
{
Start = start;
End = end;
IsHit = isHit;
}
- public IntVector2 End { get; set; }
+ public Vector2 End { get; set; }
public bool IsHit { get; set; }
- public IntVector2 Start { get; set; }
+ public Vector2 Start { get; set; }
}
private HashSet unarmedCombatants { get; } = new HashSet();
diff --git a/FrEee.WinForms/Controls/GamePanel.cs b/FrEee.WinForms/Controls/GamePanel.cs
index 8b124a00..81841f8c 100644
--- a/FrEee.WinForms/Controls/GamePanel.cs
+++ b/FrEee.WinForms/Controls/GamePanel.cs
@@ -43,10 +43,17 @@ protected override void OnPaint(PaintEventArgs pe)
// http://support.microsoft.com/kb/953934
protected override void OnSizeChanged(EventArgs e)
{
- this.BeginInvoke((MethodInvoker)delegate
+ try
{
- base.OnSizeChanged(e);
- });
+ this.BeginInvoke((MethodInvoker)delegate
+ {
+ base.OnSizeChanged(e);
+ });
+ }
+ catch
+ {
+ // UI must not be set up yet
+ }
}
private void GamePanel_SizeChanged(object sender, EventArgs e)
diff --git a/FrEee.WinForms/FrEee.WinForms.csproj b/FrEee.WinForms/FrEee.WinForms.csproj
index 5bf79526..5bd035ce 100644
--- a/FrEee.WinForms/FrEee.WinForms.csproj
+++ b/FrEee.WinForms/FrEee.WinForms.csproj
@@ -8,12 +8,12 @@
-
-
-
-
+
+
+
+
-
+
diff --git a/FrEee/Extensions/AbilityExtensions.cs b/FrEee/Extensions/AbilityExtensions.cs
index be1e3b0e..c80e3166 100644
--- a/FrEee/Extensions/AbilityExtensions.cs
+++ b/FrEee/Extensions/AbilityExtensions.cs
@@ -159,8 +159,8 @@ public static IEnumerable Ancestors(this IAbilityObject obj, Fun
/// true if successful or unnecessary, otherwise false
public static bool BurnSupplies(this Ability a)
{
- if (a.Container is Component)
- return (a.Container as Component).BurnSupplies();
+ if (a.Container is Component comp)
+ return comp.BurnSupplies();
else
return true; // other ability containers don't use supplies
}
@@ -261,9 +261,9 @@ private static IEnumerable FindSharedAbilities(this IOwnableAbilityObje
var rule = clause.AbilityRule;
if (rule.CanTarget(obj.AbilityTarget))
{
- if (rule.CanTarget(AbilityTargets.Sector) && obj is ILocated)
+ if (rule.CanTarget(AbilityTargets.Sector) && obj is ILocated locObj)
{
- var sector = ((ILocated)obj).Sector;
+ var sector = locObj.Sector;
foreach (var emp in Galaxy.Current.Empires.Where(emp => emp != null))
{
foreach (var abil in sector.EmpireAbilities(emp))
@@ -307,7 +307,7 @@ private static IEnumerable FindSharedAbilities(this IOwnableAbilityObje
///
///
///
- public static string GetEmpireAbilityValue(this ICommonAbilityObject obj, Empire emp, string name, int index = 1, Func filter = null)
+ public static string? GetEmpireAbilityValue(this ICommonAbilityObject obj, Empire emp, string name, int index = 1, Func filter = null)
{
if (obj == null)
return null;
diff --git a/FrEee/Extensions/CommonExtensions.cs b/FrEee/Extensions/CommonExtensions.cs
index 8e6d2fa4..8801d328 100644
--- a/FrEee/Extensions/CommonExtensions.cs
+++ b/FrEee/Extensions/CommonExtensions.cs
@@ -254,7 +254,7 @@ public static void DealWithMines(this ISpaceObject sobj)
///
///
///
- public static T Default(this object value, T def = default, bool throwIfWrongType = false)
+ public static T? Default(this object value, T? def = default, bool throwIfWrongType = false)
{
if (throwIfWrongType && !(value is T))
throw new InvalidCastException($"Cannot convert {value} to type {typeof(T)}.");
@@ -701,7 +701,7 @@ public static object Instantiate(this Type type, params object[] args)
if (type.Name == "Battle")
return typeof(SpaceBattle).Instantiate(); // HACK - old savegame compatibility
if (type.GetConstructors().Where(c => c.GetParameters().Length == (args == null ? 0 : args.Length)).Any())
- return Activator.CreateInstance(type, args);
+ return Activator.CreateInstance(type, args) ?? throw new NullReferenceException($"Couldn't create instance of type {type}.");
else
return FormatterServices.GetSafeUninitializedObject(type);
}
@@ -903,11 +903,10 @@ public static int NormalizeSupplies(this IMobileSpaceObject sobj)
///
///
///
- public static T Parse(this string s)
+ public static T Parse(this string s, IFormatProvider provider = null)
+ where T : IParsable
{
- var parser = typeof(T).GetMethod("Parse", BindingFlags.Static);
- var expr = Expression.Call(parser);
- return (T)expr.Method.Invoke(null, new object[] { s });
+ return T.Parse(s, provider);
}
///
@@ -1054,10 +1053,14 @@ public static string ReadTo(this TextReader r, char c, StringBuilder log)
public static string ReadToEndOfLine(this TextReader r, char c, StringBuilder log)
{
var sb = new StringBuilder();
- string data = "";
+ string? data = "";
do
{
data = r.ReadLine();
+ if (data is null)
+ {
+ throw new Exception($"Found end of text when looking for character '{c}'.");
+ }
log?.Append(data);
sb.Append(data);
if (data.EndsWith(c.ToString()))
diff --git a/FrEee/Extensions/ConversionExtensions.cs b/FrEee/Extensions/ConversionExtensions.cs
index 315fa8c0..e8b022af 100644
--- a/FrEee/Extensions/ConversionExtensions.cs
+++ b/FrEee/Extensions/ConversionExtensions.cs
@@ -39,9 +39,9 @@ public static double AngleTo(this PointF p, PointF target)
///
///
///
- public static T CastTo(this object o, T defaultValue = default(T))
+ public static T? CastTo(this object o, T? defaultValue = default)
{
- return (T)((o ?? defaultValue) ?? default(T));
+ return (T?)((o ?? defaultValue) ?? default);
}
///
diff --git a/FrEee/FrEee.csproj b/FrEee/FrEee.csproj
index 28cc8bf4..e5feea6a 100644
--- a/FrEee/FrEee.csproj
+++ b/FrEee/FrEee.csproj
@@ -8,12 +8,13 @@
-
-
+
+
-
-
-
+
+
+
+
diff --git a/FrEee/Modding/Templates/ComponentTemplate.cs b/FrEee/Modding/Templates/ComponentTemplate.cs
index 158aa386..5ef2f619 100644
--- a/FrEee/Modding/Templates/ComponentTemplate.cs
+++ b/FrEee/Modding/Templates/ComponentTemplate.cs
@@ -330,7 +330,7 @@ public override int GetHashCode()
return ModID.GetHashCode();
}
- public static bool operator ==(ComponentTemplate t1, ComponentTemplate t2)
+ public static bool operator ==(ComponentTemplate? t1, ComponentTemplate? t2)
{
if (t1 is null && t2 is null)
return true;
diff --git a/FrEee/Modding/Templates/GalaxyTemplate.cs b/FrEee/Modding/Templates/GalaxyTemplate.cs
index ea682f18..cd4ff5ab 100644
--- a/FrEee/Modding/Templates/GalaxyTemplate.cs
+++ b/FrEee/Modding/Templates/GalaxyTemplate.cs
@@ -131,7 +131,7 @@ public Galaxy Instantiate(Status status, double desiredProgress, PRNG dice)
sys.Name = unusedNames.PickRandom(dice);
unusedNames.Remove(sys.Name);
NameStellarObjects(sys);
- gal.StarSystemLocations.Add(new ObjectLocation { Location = p.Value, Item = sys });
+ gal.StarSystemLocations.Add(new(sys, p.Value));
if (status != null)
status.Progress += progressPerStarSystem;
}
diff --git a/FrEee/Objects/Civilization/Empire.cs b/FrEee/Objects/Civilization/Empire.cs
index 86bf4f80..3fe851d1 100644
--- a/FrEee/Objects/Civilization/Empire.cs
+++ b/FrEee/Objects/Civilization/Empire.cs
@@ -835,14 +835,14 @@ public Visibility CheckVisibility(Empire emp)
return Visibility.Unknown;
}
- public int CompareTo(Empire other)
+ public int CompareTo(Empire? other)
{
- return Name.CompareTo(other.Name);
+ return Name.CompareTo(other?.Name);
}
- public int CompareTo(object obj)
+ public int CompareTo(object? obj)
{
- return Name.CompareTo(obj.ToString());
+ return Name.CompareTo(obj?.ToString());
}
///
diff --git a/FrEee/Objects/Combat/Grid/Battle.cs b/FrEee/Objects/Combat/Grid/Battle.cs
index 35bab934..22c7db6d 100644
--- a/FrEee/Objects/Combat/Grid/Battle.cs
+++ b/FrEee/Objects/Combat/Grid/Battle.cs
@@ -128,7 +128,7 @@ public IEnumerable IconPaths
public IList Log { get; private set; }
- public IList LowerRight { get; private set; } = new List();
+ public IList> LowerRight { get; private set; } = new List>();
public abstract int MaxRounds { get; }
@@ -162,7 +162,7 @@ public IEnumerable PortraitPaths
public double Timestamp { get; private set; }
- public IList UpperLeft { get; private set; } = new List();
+ public IList> UpperLeft { get; private set; } = new List>();
public int GetDiameter(int round)
{
@@ -175,7 +175,7 @@ public virtual void Initialize(IEnumerable combatants)
StartCombatants = combatants.Select(c => new { ID = c.ID, Copy = c.CopyAndAssignNewID() }).ToDictionary(q => q.ID, q => q.Copy);
}
- public abstract void PlaceCombatants(SafeDictionary locations);
+ public abstract void PlaceCombatants(SafeDictionary> locations);
///
/// Resolves the battle.
@@ -189,7 +189,7 @@ public void Resolve()
Current.Add(this);
var reloads = new SafeDictionary();
- var locations = new SafeDictionary();
+ var locations = new SafeDictionary>();
PlaceCombatants(locations);
@@ -250,7 +250,7 @@ int GetCombatSpeedThisRound(ICombatant c)
continue;
}
s.DistanceTraveled += Math.Min(GetCombatSpeedThisRound(c), locations[s].DistanceToEightWay(locations[s.Target]));
- locations[s] = IntVector2.InterpolateEightWay(locations[s], locations[s.Target], GetCombatSpeedThisRound(c));
+ locations[s] = Vector2.InterpolateEightWay(locations[s], locations[s.Target], GetCombatSpeedThisRound(c));
if (s.DistanceTraveled > s.WeaponInfo.MaxRange)
{
s.Hitpoints = 0;
@@ -338,16 +338,16 @@ int GetCombatSpeedThisRound(ICombatant c)
maxdmgrange = 0;
}
var targetPos = locations[bestTarget];
- var tiles = new HashSet();
+ var tiles = new HashSet>();
for (var x = targetPos.X - maxdmgrange; x <= targetPos.X + maxdmgrange; x++)
{
- tiles.Add(new IntVector2(x, targetPos.Y - maxdmgrange));
- tiles.Add(new IntVector2(x, targetPos.Y + maxdmgrange));
+ tiles.Add(new Vector2(x, targetPos.Y - maxdmgrange));
+ tiles.Add(new Vector2(x, targetPos.Y + maxdmgrange));
}
for (var y = targetPos.Y - maxdmgrange; y <= targetPos.Y + maxdmgrange; y++)
{
- tiles.Add(new IntVector2(targetPos.X - maxdmgrange, y));
- tiles.Add(new IntVector2(targetPos.X + maxdmgrange, y));
+ tiles.Add(new Vector2(targetPos.X - maxdmgrange, y));
+ tiles.Add(new Vector2(targetPos.X + maxdmgrange, y));
}
if (c.FillsCombatTile)
{
@@ -360,7 +360,7 @@ int GetCombatSpeedThisRound(ICombatant c)
if (tiles.Any())
{
var closest = tiles.WithMin(t => t.DistanceToEightWay(locations[c])).First();
- locations[c] = IntVector2.InterpolateEightWay(locations[c], closest, GetCombatSpeedThisRound(c), vec => locations.Values.Contains(vec));
+ locations[c] = Vector2.InterpolateEightWay(locations[c], closest, GetCombatSpeedThisRound(c), vec => locations.Values.Contains(vec));
var newdist = locations[c].DistanceToEightWay(locations[bestTarget]);
if (DistancesToTargets.ContainsKey(c) && newdist >= DistancesToTargets[c] && combatSpeeds[c] <= combatSpeeds[bestTarget] && !c.Weapons.Any(w => w.Template.WeaponMaxRange >= newdist))
{
@@ -430,7 +430,7 @@ int GetCombatSpeedThisRound(ICombatant c)
var w = info.Item2.Weapons.ElementAt(ix);
var wc = StartCombatants[info.Item2.ID].Weapons.ElementAt(ix);
}
- locations[info.Launchee] = new IntVector2(locations[info.Launcher]);
+ locations[info.Launchee] = new Vector2(locations[info.Launcher]);
Events.Last().Add(new CombatantLaunchedEvent(this, info.Launcher, info.Launchee, locations[info.Launchee]));
}
}
@@ -588,7 +588,7 @@ public override string ToString()
return Name;
}
- private void CheckSeekerDetonation(Seeker s, SafeDictionary locations)
+ private void CheckSeekerDetonation(Seeker s, SafeDictionary> locations)
{
if (locations[s] == locations[s.Target])
{
@@ -611,7 +611,7 @@ private void CheckSeekerDetonation(Seeker s, SafeDictionary reloads, SafeDictionary locations, SafeDictionary> multiplex)
+ private void TryFireWeapon(ICombatant c, Component w, SafeDictionary reloads, SafeDictionary> locations, SafeDictionary> multiplex)
{
// find suitable targets in range
ICombatant target;
@@ -676,7 +676,7 @@ private void TryFireWeapon(ICombatant c, Component w, SafeDictionary(locations[c]);
Events.Last().Add(new CombatantLaunchedEvent(this, c, seeker, locations[seeker]));
}
else
@@ -731,16 +731,16 @@ private void TryFireWeapon(ICombatant c, Component w, SafeDictionary positions)
+ private void UpdateBounds(int round, IEnumerable> positions)
{
while (UpperLeft.Count() <= round)
- UpperLeft.Add(new IntVector2());
+ UpperLeft.Add(new Vector2());
while (LowerRight.Count() <= round)
- LowerRight.Add(new IntVector2());
- UpperLeft[round].X = positions.MinOrDefault(q => q.X);
- LowerRight[round].X = positions.MaxOrDefault(q => q.X);
- UpperLeft[round].Y = positions.MinOrDefault(q => q.Y);
- LowerRight[round].Y = positions.MaxOrDefault(q => q.Y);
+ LowerRight.Add(new Vector2());
+ UpperLeft[round] = UpperLeft[round] with { X = positions.MinOrDefault(q => q.X) };
+ LowerRight[round] = LowerRight[round] with { X = positions.MaxOrDefault(q => q.X) };
+ UpperLeft[round] = UpperLeft[round] with { Y = positions.MinOrDefault(q => q.Y) };
+ LowerRight[round] = LowerRight[round] with { Y = positions.MaxOrDefault(q => q.Y) };
}
public void Dispose()
diff --git a/FrEee/Objects/Combat/Grid/BattleEvent.cs b/FrEee/Objects/Combat/Grid/BattleEvent.cs
index 28a3c89d..6a38c1a2 100644
--- a/FrEee/Objects/Combat/Grid/BattleEvent.cs
+++ b/FrEee/Objects/Combat/Grid/BattleEvent.cs
@@ -7,7 +7,7 @@ namespace FrEee.Objects.Combat.Grid;
public abstract class BattleEvent : IBattleEvent
{
- protected BattleEvent(IBattle battle, ICombatant combatant, IntVector2 startPosition, IntVector2 endPosition)
+ protected BattleEvent(IBattle battle, ICombatant combatant, Vector2 startPosition, Vector2 endPosition)
{
Battle = battle;
Combatant = combatant;
@@ -27,7 +27,7 @@ public ICombatant Combatant
set => combatant = value.ReferViaGalaxy();
}
- public IntVector2 EndPosition { get; set; }
+ public Vector2 EndPosition { get; set; }
- public IntVector2 StartPosition { get; set; }
+ public Vector2 StartPosition { get; set; }
}
diff --git a/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs b/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs
index 7e8661a2..523656f8 100644
--- a/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs
+++ b/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs
@@ -6,7 +6,7 @@ namespace FrEee.Objects.Combat.Grid;
public class CombatantAppearsEvent : BattleEvent
{
- public CombatantAppearsEvent(IBattle battle, ICombatant combatant, IntVector2 position)
+ public CombatantAppearsEvent(IBattle battle, ICombatant combatant, Vector2 position)
: base(battle, combatant, position, position)
{
IsUnarmed = !(Combatant is Seeker) && !Combatant.Weapons.Any();
diff --git a/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs b/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs
index aed43a8e..fc8bf27c 100644
--- a/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs
+++ b/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs
@@ -4,7 +4,7 @@ namespace FrEee.Objects.Combat.Grid;
public class CombatantDestroyedEvent : BattleEvent
{
- public CombatantDestroyedEvent(IBattle battle, ICombatant combatant, IntVector2 position)
+ public CombatantDestroyedEvent(IBattle battle, ICombatant combatant, Vector2 position)
: base(battle, combatant, position, position)
{
}
diff --git a/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs b/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs
index 98224c88..6424e1dd 100644
--- a/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs
+++ b/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs
@@ -7,7 +7,7 @@ namespace FrEee.Objects.Combat.Grid;
[Obsolete("This class is deprecated; use CombatantDestroyedEvent if a combatant is destroyed.")]
public class CombatantDisappearsEvent : BattleEvent
{
- public CombatantDisappearsEvent(IBattle battle, ICombatant combatant, IntVector2 position)
+ public CombatantDisappearsEvent(IBattle battle, ICombatant combatant, Vector2 position)
: base(battle, combatant, position, position)
{
}
diff --git a/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs b/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs
index 6b744f88..8bfd3654 100644
--- a/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs
+++ b/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs
@@ -6,7 +6,7 @@ namespace FrEee.Objects.Combat.Grid;
public class CombatantLaunchedEvent : BattleEvent
{
- public CombatantLaunchedEvent(Battle battle, ICombatant launcher, ICombatant combatant, IntVector2 position)
+ public CombatantLaunchedEvent(Battle battle, ICombatant launcher, ICombatant combatant, Vector2 position)
: base(battle, combatant, position, position)
{
Launcher = launcher;
diff --git a/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs b/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs
index 561c25f1..6921ab93 100644
--- a/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs
+++ b/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs
@@ -4,7 +4,7 @@ namespace FrEee.Objects.Combat.Grid;
public class CombatantMovesEvent : BattleEvent
{
- public CombatantMovesEvent(Battle battle, ICombatant combatant, IntVector2 here, IntVector2 there)
+ public CombatantMovesEvent(Battle battle, ICombatant combatant, Vector2 here, Vector2 there)
: base(battle, combatant, here, there)
{
}
diff --git a/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs b/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs
index 519fc859..64e30d20 100644
--- a/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs
+++ b/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs
@@ -10,7 +10,7 @@ namespace FrEee.Objects.Combat.Grid;
///
public class CombatantsCollideEvent : BattleEvent
{
- public CombatantsCollideEvent(Battle battle, ICombatant combatant, ICombatant target, IntVector2 location, int combatantDamage, int targetDamage, bool wasCombatantDisarmed, bool wasTargetDisarmed)
+ public CombatantsCollideEvent(Battle battle, ICombatant combatant, ICombatant target, Vector2 location, int combatantDamage, int targetDamage, bool wasCombatantDisarmed, bool wasTargetDisarmed)
: base(battle, combatant, location, location)
{
Target = target;
diff --git a/FrEee/Objects/Combat/Grid/GroundBattle.cs b/FrEee/Objects/Combat/Grid/GroundBattle.cs
index f3ff6f9d..2b3d45f3 100644
--- a/FrEee/Objects/Combat/Grid/GroundBattle.cs
+++ b/FrEee/Objects/Combat/Grid/GroundBattle.cs
@@ -53,11 +53,11 @@ public override void Initialize(IEnumerable combatants)
Dice = new PRNG((int)(moduloID / Galaxy.Current.Timestamp * 10));
}
- public override void PlaceCombatants(SafeDictionary locations)
+ public override void PlaceCombatants(SafeDictionary> locations)
{
// in ground combat, for now everyone is right on top of each other
foreach (var c in Combatants)
- locations.Add(c, new IntVector2());
+ locations.Add(c, new Vector2());
}
public override int MaxRounds => Mod.Current.Settings.GroundCombatTurns;
diff --git a/FrEee/Objects/Combat/Grid/IBattleEvent.cs b/FrEee/Objects/Combat/Grid/IBattleEvent.cs
index 87876ffb..923b398f 100644
--- a/FrEee/Objects/Combat/Grid/IBattleEvent.cs
+++ b/FrEee/Objects/Combat/Grid/IBattleEvent.cs
@@ -6,6 +6,6 @@ public interface IBattleEvent
{
IBattle Battle { get; }
ICombatant Combatant { get; }
- IntVector2 EndPosition { get; }
- IntVector2 StartPosition { get; }
+ Vector2 EndPosition { get; }
+ Vector2 StartPosition { get; }
}
\ No newline at end of file
diff --git a/FrEee/Objects/Combat/Grid/SpaceBattle.cs b/FrEee/Objects/Combat/Grid/SpaceBattle.cs
index 5663eab9..52554d2b 100644
--- a/FrEee/Objects/Combat/Grid/SpaceBattle.cs
+++ b/FrEee/Objects/Combat/Grid/SpaceBattle.cs
@@ -40,7 +40,7 @@ public override void Initialize(IEnumerable combatants)
Dice = new PRNG((int)(moduloID / Galaxy.Current.Timestamp * 10));
}
- public override void PlaceCombatants(SafeDictionary locations)
+ public override void PlaceCombatants(SafeDictionary> locations)
{
if (Sector.SpaceObjects.OfType().Any())
{
@@ -69,7 +69,7 @@ public override void PlaceCombatants(SafeDictionary loca
}
}
- private void PlaceCombatant(SafeDictionary locations, double x, double y, ICombatant comb)
+ private void PlaceCombatant(SafeDictionary> locations, double x, double y, ICombatant comb)
{
// scramble all tile-filling combatants in rings around the largest
if (comb.FillsCombatTile)
@@ -77,13 +77,13 @@ private void PlaceCombatant(SafeDictionary locations, do
for (int r = 0; ; r++)
{
bool done = false;
- var tiles = IntVector2.AtRadius(r);
+ var tiles = Vector2Utility.AtRadius(r);
foreach (var tile in tiles.Shuffle(Dice))
{
var atHere = locations.Where(q => q.Key.FillsCombatTile && q.Value == tile);
if (!atHere.Any())
{
- locations.Add(comb, new IntVector2((int)x + tile.X, (int)y + tile.Y));
+ locations.Add(comb, new Vector2((int)x + tile.X, (int)y + tile.Y));
done = true;
break;
}
@@ -93,7 +93,7 @@ private void PlaceCombatant(SafeDictionary locations, do
}
}
else // put non-filling combatants in the center
- locations.Add(comb, new IntVector2((int)x, (int)y));
+ locations.Add(comb, new Vector2((int)x, (int)y));
}
///
diff --git a/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs b/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs
index 507950f5..0cda31c2 100644
--- a/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs
+++ b/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs
@@ -7,7 +7,7 @@ namespace FrEee.Objects.Combat.Grid;
public class WeaponFiresEvent : BattleEvent
{
- public WeaponFiresEvent(Battle battle, ICombatant attacker, IntVector2 here, ICombatant target, IntVector2 there, Component weapon, Hit hit, bool wasTargetDisarmed)
+ public WeaponFiresEvent(Battle battle, ICombatant attacker, Vector2 here, ICombatant target, Vector2 there, Component weapon, Hit hit, bool wasTargetDisarmed)
: base(battle, attacker, here, there)
{
Attacker = attacker;
diff --git a/FrEee/Objects/Space/ObjectLocation.cs b/FrEee/Objects/Space/ObjectLocation.cs
index fbe3cc6a..cec08cc3 100644
--- a/FrEee/Objects/Space/ObjectLocation.cs
+++ b/FrEee/Objects/Space/ObjectLocation.cs
@@ -8,49 +8,8 @@ namespace FrEee.Objects.Space;
/// An item and its location.
///
[Serializable]
-public class ObjectLocation
+public record ObjectLocation(T Item, Point Location)
{
- public ObjectLocation()
- {
- }
-
- public ObjectLocation(T item, Point location)
- {
- Item = item;
- Location = location;
- }
-
- public T Item { get; set; }
- public Point Location { get; set; }
-
- public static bool operator !=(ObjectLocation l1, ObjectLocation l2)
- {
- return !(l1 == l2);
- }
-
- public static bool operator ==(ObjectLocation l1, ObjectLocation l2)
- {
- if (object.ReferenceEquals(l1, l2))
- return true;
- if (l1 is null || l2 is null)
- return false;
- if (object.ReferenceEquals(l1, l2))
- return l1.Location.Equals(l2.Location);
- if (object.ReferenceEquals(l1.Item, null) || object.ReferenceEquals(l2.Item, null))
- return false;
- return l1.Item.Equals(l2.Item) && l1.Location.Equals(l2.Location);
- }
-
- public override bool Equals(object? obj)
- {
- return this == obj as ObjectLocation;
- }
-
- public override int GetHashCode()
- {
- return HashCodeMasher.Mash(Item, Location);
- }
-
public override string ToString()
{
return "(" + Location.X + ", " + Location.Y + ")" + ": " + Item;
diff --git a/FrEee/Objects/Technology/MountedComponentTemplate.cs b/FrEee/Objects/Technology/MountedComponentTemplate.cs
index 603b0a18..0ce1ec71 100644
--- a/FrEee/Objects/Technology/MountedComponentTemplate.cs
+++ b/FrEee/Objects/Technology/MountedComponentTemplate.cs
@@ -405,9 +405,9 @@ public override string ToString()
///
public class SimpleEqualityComparer : IEqualityComparer
{
- public bool Equals(MountedComponentTemplate x, MountedComponentTemplate y)
+ public bool Equals(MountedComponentTemplate? x, MountedComponentTemplate? y)
{
- return x.ComponentTemplate == y.ComponentTemplate && x.Mount == y.Mount;
+ return x?.ComponentTemplate == y?.ComponentTemplate && x?.Mount == y?.Mount;
}
public int GetHashCode(MountedComponentTemplate obj)
diff --git a/FrEee/Objects/Vehicles/Design.cs b/FrEee/Objects/Vehicles/Design.cs
index b94c327b..16a6d2a9 100644
--- a/FrEee/Objects/Vehicles/Design.cs
+++ b/FrEee/Objects/Vehicles/Design.cs
@@ -761,6 +761,11 @@ public override bool Equals(object? obj)
return false;
}
+ public override int GetHashCode()
+ {
+ return HashCodeMasher.Mash(BaseName, Hull, HashCodeMasher.MashList(Components));
+ }
+
///
/// A design is unlocked if its hull and all used mounts/components are unlocked.
///
diff --git a/FrEee/Utility/HeatMap.cs b/FrEee/Utility/HeatMap.cs
index 34d7d184..ac2c2ed4 100644
--- a/FrEee/Utility/HeatMap.cs
+++ b/FrEee/Utility/HeatMap.cs
@@ -4,21 +4,21 @@
/// A heatmap, used for pathfinding and the like.
/// Maps points on a plane to heat values with positive values being hotter.
///
-public class HeatMap : SafeDictionary
+public class HeatMap : SafeDictionary, double>
{
public double this[int x, int y]
{
- get => this[new IntVector2(x, y)];
- set => this[new IntVector2(x, y)] = value;
+ get => this[new Vector2(x, y)];
+ set => this[new Vector2(x, y)] = value;
}
- public void AddLinearGradientEightWay(IntVector2 pos, double startValue, int range, double deltaPerDistance)
+ public void AddLinearGradientEightWay(Vector2 pos, double startValue, int range, double deltaPerDistance)
{
for (var x = pos.X - range; x <= pos.X + range; x++)
{
for (var y = pos.Y - range; y <= pos.Y + range; y++)
{
- var pos2 = new IntVector2(x, y);
+ var pos2 = new Vector2(x, y);
var dist = (pos2 - pos).LengthEightWay;
var val = startValue + deltaPerDistance * dist;
this[pos2] += val;
@@ -26,29 +26,29 @@ public void AddLinearGradientEightWay(IntVector2 pos, double startValue, int ran
}
}
- public IntVector2 FindMax(IntVector2 pos, int range)
+ public Vector2 FindMax(Vector2 pos, int range)
{
- IntVector2 result = null;
+ Vector2? result = null;
for (var x = pos.X - range; x <= pos.X + range; x++)
{
for (var y = pos.Y - range; y <= pos.Y + range; y++)
{
- if (this.ContainsKey(new IntVector2(x, y)) && (result == null || this[x, y] > this[result]))
- result = new IntVector2(x, y);
+ if (this.ContainsKey(new Vector2(x, y)) && (result == null || this[x, y] > this[result]))
+ result = new Vector2(x, y);
}
}
return result ?? pos;
}
- public IntVector2 FindMin(IntVector2 pos, int range)
+ public Vector2 FindMin(Vector2 pos, int range)
{
- IntVector2 result = null;
+ Vector2? result = null;
for (var x = pos.X - range; x <= pos.X + range; x++)
{
for (var y = pos.Y - range; y <= pos.Y + range; y++)
{
- if (this.ContainsKey(new IntVector2(x, y)) && (result == null || this[x, y] < this[result]))
- result = new IntVector2(x, y);
+ if (this.ContainsKey(new Vector2(x, y)) && (result == null || this[x, y] < this[result]))
+ result = new Vector2(x, y);
}
}
return result ?? pos;
diff --git a/FrEee/Utility/IntVector2.cs b/FrEee/Utility/IntVector2.cs
deleted file mode 100644
index d1c8819b..00000000
--- a/FrEee/Utility/IntVector2.cs
+++ /dev/null
@@ -1,193 +0,0 @@
-using FrEee.Extensions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace FrEee.Utility;
-
-///
-/// A two dimensional vector of integers.
-///
-public class IntVector2 : Vector2, IEquatable
-{
- public IntVector2()
- : base()
- {
- }
-
- public IntVector2(int x, int y)
- : base(x, y)
- {
- }
-
- public IntVector2(Vector2 v)
- : base(v)
- {
- }
-
- ///
- /// Length of this vector moving in 8 directions along the grid.
- ///
- public int LengthEightWay
- {
- get
- {
- var dx = Math.Abs(X);
- var dy = Math.Abs(Y);
- return Math.Max(dx, dy);
- }
- }
-
- ///
- /// Length of this vector moving only in the 4 cardinal directions.
- ///
- public int LengthManhattan
- {
- get
- {
- var dx = Math.Abs(X);
- var dy = Math.Abs(Y);
- return dx + dy;
- }
- }
-
- ///
- /// Interpolates between two vectors.
- ///
- ///
- ///
- ///
- /// Should we skip moving to the endpoint? (say because it is occupied...)
- ///
- public static IntVector2 InterpolateEightWay(IntVector2 start, IntVector2 end, int distance, Func skip = null)
- {
- if (distance <= 0)
- return start;
- var trip = end - start;
- var tripLength = trip.LengthEightWay;
- if (distance >= tripLength)
- return end;
- var dx = trip.X;
- var dy = trip.Y;
- var pos = new IntVector2(start);
- var remainingDistance = distance;
- while (remainingDistance > 0)
- {
- if (dx == 0 && dy == 0)
- break;
- if (dx != 0)
- {
- pos.X += Math.Sign(dx);
- dx -= Math.Sign(dx);
- }
- if (dy != 0)
- {
- pos.Y += Math.Sign(dy);
- dy -= Math.Sign(dy);
- }
- remainingDistance--;
- }
- if (skip != null && skip(pos))
- return InterpolateEightWay(start, end, distance - 1, skip); // TODO - find alternate endpoint rather than just stopping the journey one tile short
- return pos;
- }
-
- public static IntVector2 operator -(IntVector2 v)
- {
- return new IntVector2(-v.X, -v.Y);
- }
-
- public static IntVector2 operator -(IntVector2 v1, IntVector2 v2)
- {
- return v1 + -v2;
- }
-
- public static bool operator !=(IntVector2 v1, IntVector2 v2)
- {
- return !(v1 == v2);
- }
-
- public static IntVector2 operator *(IntVector2 v, int s)
- {
- return new IntVector2(v.X * s, v.Y * s);
- }
-
- public static IntVector2 operator *(int s, IntVector2 v)
- {
- return v * s;
- }
-
- public static IntVector2 operator /(IntVector2 v, int s)
- {
- return new IntVector2(v.X / s, v.Y / s);
- }
-
- public static IntVector2 operator +(IntVector2 v1, IntVector2 v2)
- {
- return new IntVector2(v1.X + v2.X, v1.Y + v2.Y);
- }
-
- public static bool operator ==(IntVector2 v1, IntVector2 v2)
- {
- return v1.SafeEquals(v2);
- }
-
- public int DistanceToEightWay(IntVector2 dest)
- {
- return (dest - this).LengthEightWay;
- }
-
- public int DistanceToManhattan(IntVector2 dest)
- {
- return (dest - this).LengthManhattan;
- }
-
- public override bool Equals(object? obj)
- {
- if (obj is IntVector2 v)
- return Equals(v);
- return false;
- }
-
- public bool Equals(IntVector2? other)
- {
- return other is not null && X == other.X && Y == other.Y;
- }
-
- public override int GetHashCode()
- {
- return HashCodeMasher.Mash(X, Y);
- }
-
- public override string ToString()
- {
- return $"({X}, {Y})";
- }
-
- public static IEnumerable WithinRadius(int radius)
- {
- if (!withinRadius.ContainsKey(radius))
- {
- if (radius < 0)
- withinRadius[radius] = Enumerable.Empty();
- else
- {
- var list = new List();
- for (var x = -radius; x <= radius; x++)
- {
- for (var y = -radius; y <= radius; y++)
- list.Add(new IntVector2(x, y));
- }
- withinRadius.Add(radius, list);
- }
- }
- return withinRadius[radius];
- }
-
- private static IDictionary> withinRadius = new Dictionary>();
-
- public static IEnumerable AtRadius(int radius)
- {
- return WithinRadius(radius).Except(WithinRadius(radius - 1));
- }
-}
\ No newline at end of file
diff --git a/FrEee/Utility/Vector2.cs b/FrEee/Utility/Vector2.cs
index bb1bb85f..0f8152e1 100644
--- a/FrEee/Utility/Vector2.cs
+++ b/FrEee/Utility/Vector2.cs
@@ -1,5 +1,6 @@
using FrEee.Extensions;
using System;
+using System.Numerics;
namespace FrEee.Utility;
@@ -7,29 +8,155 @@ namespace FrEee.Utility;
/// A generic two dimensional vector.
///
///
-public class Vector2 : IEquatable>
+public record Vector2(T X, T Y)
+ : IEquatable>
+ where T : INumber, ISignedNumber
{
- public Vector2()
- : this(default(T), default(T))
+ ///
+ /// Creates a vector with both values initialized to zero.
+ ///
+ public Vector2() : this(T.Zero, T.Zero) { }
+
+ ///
+ /// Copies a vector.
+ ///
+ /// The vector to copy.
+ public Vector2(Vector2 other) : base()
{
+ X = other.X;
+ Y = other.Y;
}
- public Vector2(T x, T y)
+ ///
+ /// Length of this vector moving in 8 directions along the grid.
+ ///
+ public T LengthEightWay
{
- X = x;
- Y = y;
+ get
+ {
+ var dx = T.Abs(X);
+ var dy = T.Abs(Y);
+ return T.Max(dx, dy);
+ }
}
- public Vector2(Vector2 v)
- : this(v.X, v.Y)
+ ///
+ /// Length of this vector moving only in the 4 cardinal directions.
+ ///
+ public T LengthManhattan
{
+ get
+ {
+ var dx = T.Abs(X);
+ var dy = T.Abs(Y);
+ return dx + dy;
+ }
}
- public T X { get; set; }
- public T Y { get; set; }
+ ///
+ /// Interpolates between two vectors.
+ ///
+ ///
+ ///
+ ///
+ /// Should we skip moving to the endpoint? (say because it is occupied...)
+ ///
+ public static Vector2 InterpolateEightWay(Vector2 start, Vector2 end, T distance, Func, bool> skip = null)
+ {
+ if (distance <= T.Zero)
+ return start;
+ var trip = end - start;
+ var tripLength = trip.LengthEightWay;
+ if (distance >= tripLength)
+ return end;
+ var dx = trip.X;
+ var dy = trip.Y;
+ var pos = new Vector2(start);
+ var remainingDistance = distance;
+ while (remainingDistance > T.Zero)
+ {
+ if (dx == T.Zero && dy == T.Zero)
+ break;
+ if (dx != T.Zero)
+ {
+ pos = new(pos.X + TSign(dx), pos.Y);
+ dx -= TSign(dx);
+ }
+ if (dy != T.Zero)
+ {
+ pos = new(pos.X, pos.Y + TSign(dy));
+ dy -= TSign(dy);
+ }
+ remainingDistance--;
+ }
+ if (skip != null && skip(pos))
+ return InterpolateEightWay(start, end, distance - T.One, skip); // TODO - find alternate endpoint rather than just stopping the journey one tile short
+ return pos;
+ }
+
+ public static Vector2 operator -(Vector2 v)
+ {
+ return new Vector2(-v.X, -v.Y);
+ }
+
+ public static Vector2 operator -(Vector2 v1, Vector2 v2)
+ {
+ return v1 + -v2;
+ }
+
+ public static Vector2 operator *(Vector2 v, T s)
+ {
+ return new Vector2(v.X * s, v.Y * s);
+ }
+
+ public static Vector2 operator *(T s, Vector2 v)
+ {
+ return v * s;
+ }
+
+ public static Vector2 operator /(Vector2 v, T s)
+ {
+ return new Vector2(v.X / s, v.Y / s);
+ }
+
+ public static Vector2 operator +(Vector2 v1, Vector2 v2)
+ {
+ return new Vector2(v1.X + v2.X, v1.Y + v2.Y);
+ }
+
+ public T DistanceToEightWay(Vector2 dest)
+ {
+ return (dest - this).LengthEightWay;
+ }
+
+ public T DistanceToManhattan(Vector2 dest)
+ {
+ return (dest - this).LengthManhattan;
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCodeMasher.Mash(X, Y);
+ }
+
+ public override string ToString()
+ {
+ return $"({X}, {Y})";
+ }
- public bool Equals(Vector2 other)
+ private static T TSign(T num)
{
- return X.SafeEquals(other.X) && Y.SafeEquals(other.Y);
+ if (num < T.Zero)
+ {
+ return T.NegativeOne;
+ }
+ else if (num > T.Zero)
+ {
+ return T.One;
+ }
+ else
+ {
+ return T.Zero;
+ }
}
}
\ No newline at end of file
diff --git a/FrEee/Utility/Vector2Utility.cs b/FrEee/Utility/Vector2Utility.cs
new file mode 100644
index 00000000..dde15345
--- /dev/null
+++ b/FrEee/Utility/Vector2Utility.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+
+namespace FrEee.Utility;
+
+///
+/// Utilities for .
+///
+public static class Vector2Utility
+{
+ ///
+ /// Finds vectors that are within a particular radius from the origin on an integer grid.
+ ///
+ ///
+ ///
+ public static IEnumerable> WithinRadius(T radius)
+ where T : IBinaryInteger, ISignedNumber
+ {
+ if (radius < T.Zero)
+ yield break;
+ else
+ {
+ for (var x = -radius; x <= radius; x++)
+ {
+ for (var y = -radius; y <= radius; y++)
+ yield return new(x, y);
+ }
+ }
+ }
+
+ ///
+ /// Finds vectors that are at a particular radius from the origin on an integer grid.
+ ///
+ ///
+ ///
+ public static IEnumerable> AtRadius(T radius)
+ where T : IBinaryInteger, ISignedNumber
+ {
+ // TODO: improve performance by just checking edges
+ return WithinRadius(radius).Except(WithinRadius(radius - T.One));
+ }
+}
\ No newline at end of file