1
0
Fork 0
forked from aly/qstbak
qstbak/SmartNav/SmartNav.Data/ZoneBoundaryDerivation.cs
2026-08-17 20:29:32 +10:00

199 lines
7.5 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Microsoft.Extensions.Logging;
using SmartNav.Model.Navigation;
namespace SmartNav.Data;
internal static class ZoneBoundaryDerivation
{
public static List<ZoneBoundary> Derive(IReadOnlyList<ExitRangeEntry> exitRanges, IReadOnlyDictionary<(ushort TerritoryId, uint InstanceId), PopRangeEntry> popRangeIndex, ILogger logger)
{
List<ZoneBoundary> list = new List<ZoneBoundary>();
HashSet<(ushort, uint)> hashSet = new HashSet<(ushort, uint)>();
List<ExitRangeEntry> list2 = new List<ExitRangeEntry>();
foreach (ExitRangeEntry exitRange in exitRanges)
{
if (hashSet.Contains((exitRange.SourceTerritoryId, exitRange.InstanceId)) || exitRange.DestTerritoryId == 0)
{
continue;
}
Vector3? vector = null;
if (popRangeIndex.TryGetValue((exitRange.DestTerritoryId, exitRange.DestInstanceId), out PopRangeEntry value))
{
vector = value.Position;
}
ExitRangeEntry exitRangeEntry = null;
foreach (ExitRangeEntry exitRange2 in exitRanges)
{
if (exitRange2.SourceTerritoryId == exitRange.DestTerritoryId && exitRange2.DestTerritoryId == exitRange.SourceTerritoryId && !hashSet.Contains((exitRange2.SourceTerritoryId, exitRange2.InstanceId)) && ((exitRange2.DestInstanceId != 0 && exitRange2.DestInstanceId == exitRange.ReturnInstanceId) || (exitRange.DestInstanceId != 0 && exitRange.DestInstanceId == exitRange2.ReturnInstanceId) || (exitRange2.ReturnInstanceId != 0 && exitRange2.ReturnInstanceId == exitRange.InstanceId)))
{
exitRangeEntry = exitRange2;
break;
}
}
if (exitRangeEntry == null)
{
float num = float.MaxValue;
ExitRangeEntry exitRangeEntry2 = null;
foreach (ExitRangeEntry exitRange3 in exitRanges)
{
if (exitRange3.SourceTerritoryId != exitRange.DestTerritoryId || exitRange3.DestTerritoryId != exitRange.SourceTerritoryId || hashSet.Contains((exitRange3.SourceTerritoryId, exitRange3.InstanceId)))
{
continue;
}
if (popRangeIndex.TryGetValue((exitRange.SourceTerritoryId, exitRange3.DestInstanceId), out PopRangeEntry value2))
{
float num2 = Vector3.Distance(exitRange.SourcePosition, value2.Position);
if (num2 < num)
{
num = num2;
exitRangeEntry = exitRange3;
}
}
else if ((object)exitRangeEntry2 == null)
{
exitRangeEntry2 = exitRange3;
}
}
if ((object)exitRangeEntry == null)
{
exitRangeEntry = exitRangeEntry2;
}
}
Vector3? vector2 = (((object)exitRangeEntry != null) ? new Vector3?(exitRangeEntry.SourcePosition) : vector);
if (!vector2.HasValue)
{
list2.Add(exitRange);
continue;
}
list.Add(new ZoneBoundary
{
Id = $"lgb-{exitRange.SourceTerritoryId}-{exitRange.DestTerritoryId}-{exitRange.InstanceId}",
Side1 = new BoundarySide
{
TerritoryId = exitRange.SourceTerritoryId,
Position = exitRange.SourcePosition,
Trigger = TriggerOf(exitRange)
},
Side2 = new BoundarySide
{
TerritoryId = exitRange.DestTerritoryId,
Position = vector2.Value,
Trigger = ((exitRangeEntry == null) ? null : TriggerOf(exitRangeEntry))
},
OneWay = (exitRangeEntry == null)
});
hashSet.Add((exitRange.SourceTerritoryId, exitRange.InstanceId));
if (exitRangeEntry != null)
{
hashSet.Add((exitRangeEntry.SourceTerritoryId, exitRangeEntry.InstanceId));
}
}
HashSet<(ushort, ushort)> hashSet2 = new HashSet<(ushort, ushort)>();
foreach (ZoneBoundary item in list)
{
hashSet2.Add((item.Side1.TerritoryId, item.Side2.TerritoryId));
hashSet2.Add((item.Side2.TerritoryId, item.Side1.TerritoryId));
}
foreach (ExitRangeEntry item2 in list2)
{
if (hashSet2.Contains((item2.SourceTerritoryId, item2.DestTerritoryId)))
{
logger.LogDebug("Skipped redundant unresolvable ExitRange {InstanceId} in territory {SrcTerritory} -> {DestTerritory}", item2.InstanceId, item2.SourceTerritoryId, item2.DestTerritoryId);
}
else
{
logger.LogWarning("ExitRange {InstanceId} in territory {SrcTerritory} -> {DestTerritory}: could not resolve destination position (DestInstanceId={DestId})", item2.InstanceId, item2.SourceTerritoryId, item2.DestTerritoryId, item2.DestInstanceId);
}
}
return list;
}
public static void ApplyOverrides(List<ZoneBoundary> boundaries, IReadOnlyCollection<(ushort TerritoryA, ushort TerritoryB)> flyingPairs, IReadOnlyList<ZoneBoundaryPositionOverride> positionOverrides, ILogger logger)
{
HashSet<(ushort, ushort)> hashSet = new HashSet<(ushort, ushort)>(flyingPairs);
foreach (ZoneBoundary boundary in boundaries)
{
if (hashSet.Contains(PairKey(boundary.Side1.TerritoryId, boundary.Side2.TerritoryId)))
{
boundary.RequiresFlying = true;
}
}
Dictionary<(ushort, ushort), List<ZoneBoundary>> dictionary = (from b in boundaries
group b by PairKey(b.Side1.TerritoryId, b.Side2.TerritoryId)).ToDictionary((IGrouping<(ushort, ushort), ZoneBoundary> g) => g.Key, (IGrouping<(ushort, ushort), ZoneBoundary> g) => g.ToList());
int num = 0;
foreach (IGrouping<(ushort, ushort), ZoneBoundaryPositionOverride> item in from o in positionOverrides
group o by (TerritoryA: o.TerritoryA, TerritoryB: o.TerritoryB))
{
List<ZoneBoundaryPositionOverride> list = item.ToList();
if (!dictionary.TryGetValue(item.Key, out var value))
{
num += list.Count;
logger.LogWarning("No derived boundary for override pair {TerritoryA}<->{TerritoryB} ({Count} overrides) - game data drift?", item.Key.Item1, item.Key.Item2, list.Count);
continue;
}
List<(float, int, int)> list2 = new List<(float, int, int)>();
for (int num2 = 0; num2 < value.Count; num2++)
{
for (int num3 = 0; num3 < list.Count; num3++)
{
list2.Add((OverrideDistance(value[num2], list[num3]), num2, num3));
}
}
bool[] array = new bool[value.Count];
bool[] array2 = new bool[list.Count];
foreach (var item2 in list2.OrderBy<(float, int, int), float>(((float Distance, int GateIdx, int OverrideIdx) c) => c.Distance))
{
if (!array[item2.Item2] && !array2[item2.Item3])
{
array[item2.Item2] = true;
array2[item2.Item3] = true;
ZoneBoundary zoneBoundary = value[item2.Item2];
ZoneBoundaryPositionOverride zoneBoundaryPositionOverride = list[item2.Item3];
bool flag = zoneBoundary.Side1.TerritoryId == zoneBoundaryPositionOverride.TerritoryA;
zoneBoundary.Side1.Position = (flag ? zoneBoundaryPositionOverride.PositionA : zoneBoundaryPositionOverride.PositionB);
zoneBoundary.Side2.Position = (flag ? zoneBoundaryPositionOverride.PositionB : zoneBoundaryPositionOverride.PositionA);
}
}
for (int num4 = 0; num4 < list.Count; num4++)
{
if (!array2[num4])
{
num++;
}
}
}
if (num > 0)
{
logger.LogWarning("{Count} position overrides had no gate to attach to", num);
}
static (ushort, ushort) PairKey(ushort a, ushort b)
{
if (a > b)
{
return (b, a);
}
return (a, b);
}
}
private static TriggerBox? TriggerOf(ExitRangeEntry exit)
{
if (!(exit.HalfExtents == Vector3.Zero))
{
return new TriggerBox(exit.SourcePosition, exit.Rotation, exit.HalfExtents);
}
return null;
}
private static float OverrideDistance(ZoneBoundary gate, ZoneBoundaryPositionOverride ovr)
{
if (gate.Side1.TerritoryId != ovr.TerritoryA)
{
return Vector3.Distance(gate.Side1.Position, ovr.PositionB) + Vector3.Distance(gate.Side2.Position, ovr.PositionA);
}
return Vector3.Distance(gate.Side1.Position, ovr.PositionA) + Vector3.Distance(gate.Side2.Position, ovr.PositionB);
}
}