feat: simplified local-space and celestial, removed bubbles
This commit is contained in:
@@ -5,8 +5,7 @@ public sealed class ClaimRuntime
|
||||
public required string Id { get; init; }
|
||||
public required string FactionId { get; init; }
|
||||
public required string SystemId { get; init; }
|
||||
public required string NodeId { get; init; }
|
||||
public required string BubbleId { get; init; }
|
||||
public required string CelestialId { get; init; }
|
||||
public string? CommanderId { get; set; }
|
||||
public DateTimeOffset PlacedAtUtc { get; init; }
|
||||
public DateTimeOffset ActivatesAtUtc { get; set; }
|
||||
@@ -20,8 +19,7 @@ public sealed class ConstructionSiteRuntime
|
||||
public required string Id { get; init; }
|
||||
public required string FactionId { get; init; }
|
||||
public required string SystemId { get; init; }
|
||||
public required string NodeId { get; init; }
|
||||
public required string BubbleId { get; init; }
|
||||
public required string CelestialId { get; init; }
|
||||
public required string TargetKind { get; init; }
|
||||
public required string TargetDefinitionId { get; init; }
|
||||
public string? BlueprintId { get; set; }
|
||||
|
||||
@@ -6,8 +6,6 @@ public enum SpatialNodeKind
|
||||
Planet,
|
||||
Moon,
|
||||
LagrangePoint,
|
||||
Station,
|
||||
ResourceSite,
|
||||
}
|
||||
|
||||
public enum WorkStatus
|
||||
@@ -112,7 +110,7 @@ public static class ShipTaskKinds
|
||||
public const string BuildConstructionSite = "build-construction-site";
|
||||
public const string EscortTarget = "escort-target";
|
||||
public const string AttackTarget = "attack-target";
|
||||
public const string DefendBubble = "defend-bubble";
|
||||
public const string DefendCelestial = "defend-celestial";
|
||||
public const string Retreat = "retreat";
|
||||
public const string HoldPosition = "hold-position";
|
||||
}
|
||||
@@ -167,8 +165,6 @@ public static class SimulationEnumMappings
|
||||
SpatialNodeKind.Planet => "planet",
|
||||
SpatialNodeKind.Moon => "moon",
|
||||
SpatialNodeKind.LagrangePoint => "lagrange-point",
|
||||
SpatialNodeKind.Station => "station",
|
||||
SpatialNodeKind.ResourceSite => "resource-site",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(kind), kind, null),
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ public sealed class SimulationWorld
|
||||
public required BalanceDefinition Balance { get; init; }
|
||||
public required List<SystemRuntime> Systems { get; init; }
|
||||
public required List<ResourceNodeRuntime> Nodes { get; init; }
|
||||
public required List<NodeRuntime> SpatialNodes { get; init; }
|
||||
public required List<LocalBubbleRuntime> LocalBubbles { get; init; }
|
||||
public required List<CelestialRuntime> Celestials { get; init; }
|
||||
public required List<StationRuntime> Stations { get; init; }
|
||||
public required List<ShipRuntime> Ships { get; init; }
|
||||
public required List<FactionRuntime> Factions { get; init; }
|
||||
|
||||
@@ -15,7 +15,7 @@ public sealed class ResourceNodeRuntime
|
||||
public required Vector3 Position { get; set; }
|
||||
public required string SourceKind { get; init; }
|
||||
public required string ItemId { get; init; }
|
||||
public string? AnchorNodeId { get; set; }
|
||||
public string? CelestialId { get; set; }
|
||||
public float OrbitRadius { get; init; }
|
||||
public float OrbitPhase { get; init; }
|
||||
public float OrbitInclination { get; init; }
|
||||
@@ -24,38 +24,24 @@ public sealed class ResourceNodeRuntime
|
||||
public string LastDeltaSignature { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class NodeRuntime
|
||||
public sealed class CelestialRuntime
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string SystemId { get; init; }
|
||||
public required SpatialNodeKind Kind { get; init; }
|
||||
public required Vector3 Position { get; set; }
|
||||
public required string BubbleId { get; init; }
|
||||
public float LocalSpaceRadius { get; init; }
|
||||
public string? ParentNodeId { get; set; }
|
||||
public string? OccupyingStructureId { get; set; }
|
||||
public string? OrbitReferenceId { get; set; }
|
||||
public string LastDeltaSignature { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class LocalBubbleRuntime
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string NodeId { get; init; }
|
||||
public required string SystemId { get; init; }
|
||||
public float Radius { get; init; }
|
||||
public HashSet<string> OccupantShipIds { get; } = new(StringComparer.Ordinal);
|
||||
public HashSet<string> OccupantStationIds { get; } = new(StringComparer.Ordinal);
|
||||
public HashSet<string> OccupantClaimIds { get; } = new(StringComparer.Ordinal);
|
||||
public HashSet<string> OccupantConstructionSiteIds { get; } = new(StringComparer.Ordinal);
|
||||
public string LastDeltaSignature { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public sealed class ShipSpatialStateRuntime
|
||||
{
|
||||
public string SpaceLayer { get; set; } = SpaceLayerKinds.LocalSpace;
|
||||
public required string CurrentSystemId { get; set; }
|
||||
public string? CurrentNodeId { get; set; }
|
||||
public string? CurrentBubbleId { get; set; }
|
||||
public string? CurrentCelestialId { get; set; }
|
||||
public Vector3? LocalPosition { get; set; }
|
||||
public Vector3? SystemPosition { get; set; }
|
||||
public string MovementRegime { get; set; } = MovementRegimeKinds.LocalFlight;
|
||||
|
||||
@@ -10,9 +10,7 @@ public sealed class StationRuntime
|
||||
public required Vector3 Position { get; set; }
|
||||
public float Radius { get; set; } = 24f;
|
||||
public required string FactionId { get; init; }
|
||||
public string? NodeId { get; set; }
|
||||
public string? BubbleId { get; set; }
|
||||
public string? AnchorNodeId { get; set; }
|
||||
public string? CelestialId { get; set; }
|
||||
public string? CommanderId { get; set; }
|
||||
public string? PolicySetId { get; set; }
|
||||
public List<StationModuleRuntime> Modules { get; } = [];
|
||||
|
||||
@@ -88,27 +88,26 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
private static List<ClaimRuntime> CreateClaims(
|
||||
IReadOnlyCollection<StationRuntime> stations,
|
||||
IReadOnlyCollection<NodeRuntime> nodes,
|
||||
IReadOnlyCollection<CelestialRuntime> celestials,
|
||||
DateTimeOffset nowUtc)
|
||||
{
|
||||
var stationsByAnchorNodeId = stations
|
||||
.Where((station) => station.AnchorNodeId is not null)
|
||||
.ToDictionary((station) => station.AnchorNodeId!, StringComparer.Ordinal);
|
||||
var stationsByCelestialId = stations
|
||||
.Where((station) => station.CelestialId is not null)
|
||||
.ToDictionary((station) => station.CelestialId!, StringComparer.Ordinal);
|
||||
var claims = new List<ClaimRuntime>();
|
||||
foreach (var node in nodes.Where((candidate) => candidate.Kind == SpatialNodeKind.LagrangePoint))
|
||||
foreach (var celestial in celestials.Where((c) => c.Kind == SpatialNodeKind.LagrangePoint))
|
||||
{
|
||||
if (!stationsByAnchorNodeId.TryGetValue(node.Id, out var station))
|
||||
if (!stationsByCelestialId.TryGetValue(celestial.Id, out var station))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
claims.Add(new ClaimRuntime
|
||||
{
|
||||
Id = $"claim-{node.Id}",
|
||||
Id = $"claim-{celestial.Id}",
|
||||
FactionId = station.FactionId,
|
||||
SystemId = node.SystemId,
|
||||
NodeId = node.Id,
|
||||
BubbleId = node.BubbleId,
|
||||
SystemId = celestial.SystemId,
|
||||
CelestialId = celestial.Id,
|
||||
PlacedAtUtc = nowUtc,
|
||||
ActivatesAtUtc = nowUtc.AddSeconds(8),
|
||||
State = ClaimStateKinds.Activating,
|
||||
@@ -122,7 +121,6 @@ public sealed partial class ScenarioLoader
|
||||
private static (List<ConstructionSiteRuntime> ConstructionSites, List<MarketOrderRuntime> MarketOrders) CreateConstructionSites(
|
||||
IReadOnlyCollection<StationRuntime> stations,
|
||||
IReadOnlyCollection<ClaimRuntime> claims,
|
||||
IReadOnlyCollection<NodeRuntime> nodes,
|
||||
IReadOnlyDictionary<string, ModuleRecipeDefinition> moduleRecipes)
|
||||
{
|
||||
var sites = new List<ConstructionSiteRuntime>();
|
||||
@@ -131,18 +129,12 @@ public sealed partial class ScenarioLoader
|
||||
foreach (var station in stations)
|
||||
{
|
||||
var moduleId = GetNextConstructionSiteModule(station, moduleRecipes);
|
||||
if (moduleId is null || station.AnchorNodeId is null)
|
||||
if (moduleId is null || station.CelestialId is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var anchorNode = nodes.FirstOrDefault((node) => node.Id == station.AnchorNodeId);
|
||||
if (anchorNode is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var claim = claims.FirstOrDefault((candidate) => candidate.NodeId == anchorNode.Id);
|
||||
var claim = claims.FirstOrDefault((candidate) => candidate.CelestialId == station.CelestialId);
|
||||
if (claim is null || !moduleRecipes.TryGetValue(moduleId, out var recipe))
|
||||
{
|
||||
continue;
|
||||
@@ -153,8 +145,7 @@ public sealed partial class ScenarioLoader
|
||||
Id = $"site-{station.Id}",
|
||||
FactionId = station.FactionId,
|
||||
SystemId = station.SystemId,
|
||||
NodeId = anchorNode.Id,
|
||||
BubbleId = anchorNode.BubbleId,
|
||||
CelestialId = station.CelestialId,
|
||||
TargetKind = "station-module",
|
||||
TargetDefinitionId = "station",
|
||||
BlueprintId = moduleId,
|
||||
|
||||
@@ -6,48 +6,44 @@ public sealed partial class ScenarioLoader
|
||||
{
|
||||
private static SystemSpatialGraph BuildSystemSpatialGraph(SystemRuntime system)
|
||||
{
|
||||
var nodes = new List<NodeRuntime>();
|
||||
var bubbles = new List<LocalBubbleRuntime>();
|
||||
var lagrangeNodesByPlanetIndex = new Dictionary<int, Dictionary<string, NodeRuntime>>();
|
||||
var celestials = new List<CelestialRuntime>();
|
||||
var lagrangeNodesByPlanetIndex = new Dictionary<int, Dictionary<string, CelestialRuntime>>();
|
||||
|
||||
var starNode = AddSpatialNode(
|
||||
nodes,
|
||||
bubbles,
|
||||
AddCelestial(
|
||||
celestials,
|
||||
id: $"node-{system.Definition.Id}-star",
|
||||
systemId: system.Definition.Id,
|
||||
kind: SpatialNodeKind.Star,
|
||||
position: Vector3.Zero,
|
||||
radius: MathF.Max(system.Definition.GravityWellRadius + StarBubbleRadiusPadding, 180f));
|
||||
localSpaceRadius: MathF.Max(system.Definition.GravityWellRadius + StarBubbleRadiusPadding, LocalSpaceRadius));
|
||||
|
||||
for (var planetIndex = 0; planetIndex < system.Definition.Planets.Count; planetIndex += 1)
|
||||
{
|
||||
var planet = system.Definition.Planets[planetIndex];
|
||||
var planetNodeId = $"node-{system.Definition.Id}-planet-{planetIndex + 1}";
|
||||
var planetPosition = ComputePlanetPosition(planet);
|
||||
var planetNode = AddSpatialNode(
|
||||
nodes,
|
||||
bubbles,
|
||||
var planetCelestial = AddCelestial(
|
||||
celestials,
|
||||
id: planetNodeId,
|
||||
systemId: system.Definition.Id,
|
||||
kind: SpatialNodeKind.Planet,
|
||||
position: planetPosition,
|
||||
radius: MathF.Max(planet.Size + PlanetBubbleRadiusPadding, 120f),
|
||||
parentNodeId: starNode.Id);
|
||||
localSpaceRadius: LocalSpaceRadius,
|
||||
parentNodeId: $"node-{system.Definition.Id}-star");
|
||||
|
||||
var lagrangeNodes = new Dictionary<string, NodeRuntime>(StringComparer.Ordinal);
|
||||
var lagrangeNodes = new Dictionary<string, CelestialRuntime>(StringComparer.Ordinal);
|
||||
foreach (var point in EnumeratePlanetLagrangePoints(planetPosition, planet))
|
||||
{
|
||||
var lagrangeNode = AddSpatialNode(
|
||||
nodes,
|
||||
bubbles,
|
||||
var lagrangeCelestial = AddCelestial(
|
||||
celestials,
|
||||
id: $"node-{system.Definition.Id}-planet-{planetIndex + 1}-{point.Designation.ToLowerInvariant()}",
|
||||
systemId: system.Definition.Id,
|
||||
kind: SpatialNodeKind.LagrangePoint,
|
||||
position: point.Position,
|
||||
radius: LagrangeBubbleRadius,
|
||||
parentNodeId: planetNode.Id,
|
||||
localSpaceRadius: LocalSpaceRadius,
|
||||
parentNodeId: planetCelestial.Id,
|
||||
orbitReferenceId: point.Designation);
|
||||
lagrangeNodes[point.Designation] = lagrangeNode;
|
||||
lagrangeNodes[point.Designation] = lagrangeCelestial;
|
||||
}
|
||||
|
||||
lagrangeNodesByPlanetIndex[planetIndex] = lagrangeNodes;
|
||||
@@ -61,54 +57,44 @@ public sealed partial class ScenarioLoader
|
||||
for (var moonIndex = 0; moonIndex < planet.MoonCount; moonIndex += 1)
|
||||
{
|
||||
var moonPosition = ComputeMoonPosition(planetPosition, moonOrbitRadius, moonIndex, planetIndex);
|
||||
AddSpatialNode(
|
||||
nodes,
|
||||
bubbles,
|
||||
AddCelestial(
|
||||
celestials,
|
||||
id: $"node-{system.Definition.Id}-planet-{planetIndex + 1}-moon-{moonIndex + 1}",
|
||||
systemId: system.Definition.Id,
|
||||
kind: SpatialNodeKind.Moon,
|
||||
position: moonPosition,
|
||||
radius: MoonBubbleRadiusPadding + 24f,
|
||||
parentNodeId: planetNode.Id);
|
||||
localSpaceRadius: LocalSpaceRadius,
|
||||
parentNodeId: planetCelestial.Id);
|
||||
moonOrbitRadius += 30f;
|
||||
}
|
||||
}
|
||||
|
||||
return new SystemSpatialGraph(system.Definition.Id, nodes, bubbles, lagrangeNodesByPlanetIndex);
|
||||
return new SystemSpatialGraph(system.Definition.Id, celestials, lagrangeNodesByPlanetIndex);
|
||||
}
|
||||
|
||||
private static NodeRuntime AddSpatialNode(
|
||||
ICollection<NodeRuntime> nodes,
|
||||
ICollection<LocalBubbleRuntime> bubbles,
|
||||
private static CelestialRuntime AddCelestial(
|
||||
ICollection<CelestialRuntime> celestials,
|
||||
string id,
|
||||
string systemId,
|
||||
SpatialNodeKind kind,
|
||||
Vector3 position,
|
||||
float radius,
|
||||
float localSpaceRadius,
|
||||
string? parentNodeId = null,
|
||||
string? orbitReferenceId = null)
|
||||
{
|
||||
var bubbleId = $"bubble-{id}";
|
||||
var node = new NodeRuntime
|
||||
var celestial = new CelestialRuntime
|
||||
{
|
||||
Id = id,
|
||||
SystemId = systemId,
|
||||
Kind = kind,
|
||||
Position = position,
|
||||
BubbleId = bubbleId,
|
||||
LocalSpaceRadius = localSpaceRadius,
|
||||
ParentNodeId = parentNodeId,
|
||||
OrbitReferenceId = orbitReferenceId,
|
||||
};
|
||||
|
||||
nodes.Add(node);
|
||||
bubbles.Add(new LocalBubbleRuntime
|
||||
{
|
||||
Id = bubbleId,
|
||||
NodeId = id,
|
||||
SystemId = systemId,
|
||||
Radius = radius,
|
||||
});
|
||||
return node;
|
||||
celestials.Add(celestial);
|
||||
return celestial;
|
||||
}
|
||||
|
||||
private static IEnumerable<LagrangePointPlacement> EnumeratePlanetLagrangePoints(
|
||||
@@ -165,36 +151,36 @@ public sealed partial class ScenarioLoader
|
||||
InitialStationDefinition plan,
|
||||
SystemRuntime system,
|
||||
SystemSpatialGraph graph,
|
||||
IReadOnlyCollection<NodeRuntime> existingNodes)
|
||||
IReadOnlyCollection<CelestialRuntime> existingCelestials)
|
||||
{
|
||||
if (plan.PlanetIndex is int planetIndex &&
|
||||
graph.LagrangeNodesByPlanetIndex.TryGetValue(planetIndex, out var lagrangeNodes))
|
||||
{
|
||||
var designation = ResolveLagrangeDesignation(plan.LagrangeSide);
|
||||
if (lagrangeNodes.TryGetValue(designation, out var lagrangeNode))
|
||||
if (lagrangeNodes.TryGetValue(designation, out var lagrangeCelestial))
|
||||
{
|
||||
return new StationPlacement(lagrangeNode, lagrangeNode.Position);
|
||||
return new StationPlacement(lagrangeCelestial, lagrangeCelestial.Position);
|
||||
}
|
||||
}
|
||||
|
||||
if (plan.Position is { Length: 3 })
|
||||
{
|
||||
var targetPosition = NormalizeScenarioPoint(system, plan.Position);
|
||||
var preferredNode = existingNodes
|
||||
.Where((node) => node.SystemId == system.Definition.Id && node.Kind == SpatialNodeKind.LagrangePoint)
|
||||
.OrderBy((node) => node.Position.DistanceTo(targetPosition))
|
||||
var preferredCelestial = existingCelestials
|
||||
.Where((c) => c.SystemId == system.Definition.Id && c.Kind == SpatialNodeKind.LagrangePoint)
|
||||
.OrderBy((c) => c.Position.DistanceTo(targetPosition))
|
||||
.FirstOrDefault()
|
||||
?? existingNodes
|
||||
.Where((node) => node.SystemId == system.Definition.Id)
|
||||
.OrderBy((node) => node.Position.DistanceTo(targetPosition))
|
||||
?? existingCelestials
|
||||
.Where((c) => c.SystemId == system.Definition.Id)
|
||||
.OrderBy((c) => c.Position.DistanceTo(targetPosition))
|
||||
.First();
|
||||
return new StationPlacement(preferredNode, preferredNode.Position);
|
||||
return new StationPlacement(preferredCelestial, preferredCelestial.Position);
|
||||
}
|
||||
|
||||
var fallbackNode = graph.Nodes
|
||||
.FirstOrDefault((node) => node.Kind == SpatialNodeKind.LagrangePoint && string.IsNullOrEmpty(node.OccupyingStructureId))
|
||||
?? graph.Nodes.First((node) => node.Kind == SpatialNodeKind.Planet);
|
||||
return new StationPlacement(fallbackNode, fallbackNode.Position);
|
||||
var fallbackCelestial = graph.Celestials
|
||||
.FirstOrDefault((c) => c.Kind == SpatialNodeKind.LagrangePoint && string.IsNullOrEmpty(c.OccupyingStructureId))
|
||||
?? graph.Celestials.First((c) => c.Kind == SpatialNodeKind.Planet);
|
||||
return new StationPlacement(fallbackCelestial, fallbackCelestial.Position);
|
||||
}
|
||||
|
||||
private static string ResolveLagrangeDesignation(int? lagrangeSide) => lagrangeSide switch
|
||||
@@ -204,7 +190,7 @@ public sealed partial class ScenarioLoader
|
||||
_ => "L1",
|
||||
};
|
||||
|
||||
private static NodeRuntime? ResolveResourceNodeAnchor(SystemSpatialGraph graph, ResourceNodeDefinition definition)
|
||||
private static CelestialRuntime? ResolveResourceNodeAnchor(SystemSpatialGraph graph, ResourceNodeDefinition definition)
|
||||
{
|
||||
if (definition.AnchorPlanetIndex is not int planetIndex || planetIndex < 0)
|
||||
{
|
||||
@@ -214,14 +200,14 @@ public sealed partial class ScenarioLoader
|
||||
if (definition.AnchorMoonIndex is int moonIndex && moonIndex >= 0)
|
||||
{
|
||||
var moonNodeId = $"node-{graph.SystemId}-planet-{planetIndex + 1}-moon-{moonIndex + 1}";
|
||||
return graph.Nodes.FirstOrDefault((node) => node.Id == moonNodeId);
|
||||
return graph.Celestials.FirstOrDefault((c) => c.Id == moonNodeId);
|
||||
}
|
||||
|
||||
var planetNodeId = $"node-{graph.SystemId}-planet-{planetIndex + 1}";
|
||||
return graph.Nodes.FirstOrDefault((node) => node.Id == planetNodeId);
|
||||
return graph.Celestials.FirstOrDefault((c) => c.Id == planetNodeId);
|
||||
}
|
||||
|
||||
private static Vector3 ComputeResourceNodePosition(NodeRuntime? anchorNode, ResourceNodeDefinition definition, float yPlane)
|
||||
private static Vector3 ComputeResourceNodePosition(CelestialRuntime? anchorCelestial, ResourceNodeDefinition definition, float yPlane)
|
||||
{
|
||||
var verticalOffset = MathF.Sin(DegreesToRadians(definition.InclinationDegrees)) * MathF.Min(definition.RadiusOffset * 0.04f, 25000f);
|
||||
var offset = new Vector3(
|
||||
@@ -229,12 +215,12 @@ public sealed partial class ScenarioLoader
|
||||
verticalOffset,
|
||||
MathF.Sin(definition.Angle) * definition.RadiusOffset);
|
||||
|
||||
if (anchorNode is null)
|
||||
if (anchorCelestial is null)
|
||||
{
|
||||
return new Vector3(offset.X, yPlane + offset.Y, offset.Z);
|
||||
}
|
||||
|
||||
return Add(anchorNode.Position, offset);
|
||||
return Add(anchorCelestial.Position, offset);
|
||||
}
|
||||
|
||||
private static Vector3 ComputePlanetPosition(PlanetDefinition planet)
|
||||
@@ -252,19 +238,18 @@ public sealed partial class ScenarioLoader
|
||||
return Add(planetPosition, new Vector3(MathF.Cos(angle) * orbitRadius, 0f, MathF.Sin(angle) * orbitRadius));
|
||||
}
|
||||
|
||||
private static ShipSpatialStateRuntime CreateInitialShipSpatialState(string systemId, Vector3 position, IReadOnlyCollection<NodeRuntime> nodes)
|
||||
private static ShipSpatialStateRuntime CreateInitialShipSpatialState(string systemId, Vector3 position, IReadOnlyCollection<CelestialRuntime> celestials)
|
||||
{
|
||||
var nearestNode = nodes
|
||||
.Where((node) => node.SystemId == systemId)
|
||||
.OrderBy((node) => node.Position.DistanceTo(position))
|
||||
var nearestCelestial = celestials
|
||||
.Where((c) => c.SystemId == systemId)
|
||||
.OrderBy((c) => c.Position.DistanceTo(position))
|
||||
.FirstOrDefault();
|
||||
|
||||
return new ShipSpatialStateRuntime
|
||||
{
|
||||
CurrentSystemId = systemId,
|
||||
SpaceLayer = SpaceLayerKinds.LocalSpace,
|
||||
CurrentNodeId = nearestNode?.Id,
|
||||
CurrentBubbleId = nearestNode?.BubbleId,
|
||||
CurrentCelestialId = nearestCelestial?.Id,
|
||||
LocalPosition = position,
|
||||
SystemPosition = position,
|
||||
MovementRegime = MovementRegimeKinds.LocalFlight,
|
||||
@@ -273,11 +258,10 @@ public sealed partial class ScenarioLoader
|
||||
|
||||
private sealed record SystemSpatialGraph(
|
||||
string SystemId,
|
||||
List<NodeRuntime> Nodes,
|
||||
List<LocalBubbleRuntime> Bubbles,
|
||||
Dictionary<int, Dictionary<string, NodeRuntime>> LagrangeNodesByPlanetIndex);
|
||||
List<CelestialRuntime> Celestials,
|
||||
Dictionary<int, Dictionary<string, CelestialRuntime>> LagrangeNodesByPlanetIndex);
|
||||
|
||||
private sealed record LagrangePointPlacement(string Designation, Vector3 Position);
|
||||
|
||||
private sealed record StationPlacement(NodeRuntime AnchorNode, Vector3 Position);
|
||||
private sealed record StationPlacement(CelestialRuntime AnchorCelestial, Vector3 Position);
|
||||
}
|
||||
|
||||
@@ -13,10 +13,7 @@ public sealed partial class ScenarioLoader
|
||||
private const float MinimumShipyardStock = 0f;
|
||||
private const float MinimumSystemSeparation = 3.2f;
|
||||
private const float StarBubbleRadiusPadding = 40f;
|
||||
private const float PlanetBubbleRadiusPadding = 80f;
|
||||
private const float MoonBubbleRadiusPadding = 40f;
|
||||
private const float LagrangeBubbleRadius = 150f;
|
||||
private const float ResourceBubbleRadius = 120f;
|
||||
private const float LocalSpaceRadius = 10_000f;
|
||||
private static readonly string[] GeneratedSystemNames =
|
||||
[
|
||||
"Aquila Verge",
|
||||
@@ -121,14 +118,12 @@ public sealed partial class ScenarioLoader
|
||||
(system) => BuildSystemSpatialGraph(system),
|
||||
StringComparer.Ordinal);
|
||||
|
||||
var celestials = new List<CelestialRuntime>();
|
||||
var nodes = new List<ResourceNodeRuntime>();
|
||||
var spatialNodes = new List<NodeRuntime>();
|
||||
var localBubbles = new List<LocalBubbleRuntime>();
|
||||
var nodeIdCounter = 0;
|
||||
foreach (var graph in systemGraphs.Values)
|
||||
{
|
||||
spatialNodes.AddRange(graph.Nodes);
|
||||
localBubbles.AddRange(graph.Bubbles);
|
||||
celestials.AddRange(graph.Celestials);
|
||||
}
|
||||
|
||||
foreach (var system in systemRuntimes)
|
||||
@@ -136,15 +131,15 @@ public sealed partial class ScenarioLoader
|
||||
var systemGraph = systemGraphs[system.Definition.Id];
|
||||
foreach (var node in system.Definition.ResourceNodes)
|
||||
{
|
||||
var anchorNode = ResolveResourceNodeAnchor(systemGraph, node);
|
||||
var anchorCelestial = ResolveResourceNodeAnchor(systemGraph, node);
|
||||
var resourceNode = new ResourceNodeRuntime
|
||||
{
|
||||
Id = $"node-{++nodeIdCounter}",
|
||||
SystemId = system.Definition.Id,
|
||||
Position = ComputeResourceNodePosition(anchorNode, node, balance.YPlane),
|
||||
Position = ComputeResourceNodePosition(anchorCelestial, node, balance.YPlane),
|
||||
SourceKind = node.SourceKind,
|
||||
ItemId = node.ItemId,
|
||||
AnchorNodeId = anchorNode?.Id,
|
||||
CelestialId = anchorCelestial?.Id,
|
||||
OrbitRadius = node.RadiusOffset,
|
||||
OrbitPhase = node.Angle,
|
||||
OrbitInclination = DegreesToRadians(node.InclinationDegrees),
|
||||
@@ -153,23 +148,6 @@ public sealed partial class ScenarioLoader
|
||||
};
|
||||
|
||||
nodes.Add(resourceNode);
|
||||
var bubbleId = $"bubble-{resourceNode.Id}";
|
||||
spatialNodes.Add(new NodeRuntime
|
||||
{
|
||||
Id = resourceNode.Id,
|
||||
SystemId = resourceNode.SystemId,
|
||||
Kind = SpatialNodeKind.ResourceSite,
|
||||
Position = resourceNode.Position,
|
||||
BubbleId = bubbleId,
|
||||
ParentNodeId = anchorNode?.Id,
|
||||
});
|
||||
localBubbles.Add(new LocalBubbleRuntime
|
||||
{
|
||||
Id = bubbleId,
|
||||
NodeId = resourceNode.Id,
|
||||
SystemId = resourceNode.SystemId,
|
||||
Radius = ResourceBubbleRadius,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +160,7 @@ public sealed partial class ScenarioLoader
|
||||
continue;
|
||||
}
|
||||
|
||||
var placement = ResolveStationPlacement(plan, system, systemGraphs[system.Definition.Id], spatialNodes);
|
||||
var placement = ResolveStationPlacement(plan, system, systemGraphs[system.Definition.Id], celestials);
|
||||
var station = new StationRuntime
|
||||
{
|
||||
Id = $"station-{++stationIdCounter}",
|
||||
@@ -193,31 +171,9 @@ public sealed partial class ScenarioLoader
|
||||
FactionId = plan.FactionId ?? DefaultFactionId,
|
||||
};
|
||||
|
||||
var stationNodeId = $"node-{station.Id}";
|
||||
var stationBubbleId = $"bubble-{station.Id}";
|
||||
station.NodeId = stationNodeId;
|
||||
station.BubbleId = stationBubbleId;
|
||||
station.AnchorNodeId = placement.AnchorNode.Id;
|
||||
station.CelestialId = placement.AnchorCelestial.Id;
|
||||
stations.Add(station);
|
||||
spatialNodes.Add(new NodeRuntime
|
||||
{
|
||||
Id = stationNodeId,
|
||||
SystemId = station.SystemId,
|
||||
Kind = SpatialNodeKind.Station,
|
||||
Position = station.Position,
|
||||
BubbleId = stationBubbleId,
|
||||
ParentNodeId = placement.AnchorNode.Id,
|
||||
OccupyingStructureId = station.Id,
|
||||
});
|
||||
localBubbles.Add(new LocalBubbleRuntime
|
||||
{
|
||||
Id = stationBubbleId,
|
||||
NodeId = stationNodeId,
|
||||
SystemId = station.SystemId,
|
||||
Radius = MathF.Max(160f, GetStationRadius(moduleDefinitions, station) + 60f),
|
||||
});
|
||||
localBubbles[^1].OccupantStationIds.Add(station.Id);
|
||||
placement.AnchorNode.OccupyingStructureId = station.Id;
|
||||
placement.AnchorCelestial.OccupyingStructureId = station.Id;
|
||||
|
||||
var startingModules = plan.StartingModules.Count > 0
|
||||
? plan.StartingModules
|
||||
@@ -274,7 +230,7 @@ public sealed partial class ScenarioLoader
|
||||
FactionId = formation.FactionId ?? DefaultFactionId,
|
||||
Position = position,
|
||||
TargetPosition = position,
|
||||
SpatialState = CreateInitialShipSpatialState(formation.SystemId, position, spatialNodes),
|
||||
SpatialState = CreateInitialShipSpatialState(formation.SystemId, position, celestials),
|
||||
DefaultBehavior = CreateBehavior(definition, formation.SystemId, scenario, patrolRoutes, refinery),
|
||||
ControllerTask = new ControllerTaskRuntime { Kind = ControllerTaskKind.Idle, Threshold = balance.ArrivalThreshold, Status = WorkStatus.Pending },
|
||||
Health = definition.MaxHealth,
|
||||
@@ -287,8 +243,8 @@ public sealed partial class ScenarioLoader
|
||||
var policies = CreatePolicies(factions);
|
||||
var commanders = CreateCommanders(factions, stations, shipsRuntime);
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var claims = CreateClaims(stations, spatialNodes, now);
|
||||
var (constructionSites, marketOrders) = CreateConstructionSites(stations, claims, spatialNodes, moduleRecipeDefinitions);
|
||||
var claims = CreateClaims(stations, celestials, now);
|
||||
var (constructionSites, marketOrders) = CreateConstructionSites(stations, claims, moduleRecipeDefinitions);
|
||||
|
||||
return new SimulationWorld
|
||||
{
|
||||
@@ -296,9 +252,8 @@ public sealed partial class ScenarioLoader
|
||||
Seed = WorldSeed,
|
||||
Balance = balance,
|
||||
Systems = systemRuntimes,
|
||||
Celestials = celestials,
|
||||
Nodes = nodes,
|
||||
SpatialNodes = spatialNodes,
|
||||
LocalBubbles = localBubbles,
|
||||
Stations = stations,
|
||||
Ships = shipsRuntime,
|
||||
Factions = factions,
|
||||
|
||||
@@ -51,8 +51,9 @@ public sealed partial class SimulationEngine
|
||||
return "none";
|
||||
}
|
||||
|
||||
var targetPosition = task.TargetPosition.Value;
|
||||
var targetNode = ResolveTravelTargetNode(world, task, targetPosition);
|
||||
// Resolve live position each frame — entities like stations orbit celestials and move every tick
|
||||
var targetPosition = ResolveCurrentTargetPosition(world, task);
|
||||
var targetCelestial = ResolveTravelTargetCelestial(world, task, targetPosition);
|
||||
ship.TargetPosition = targetPosition;
|
||||
|
||||
if (ship.SystemId != task.TargetSystemId)
|
||||
@@ -63,63 +64,83 @@ public sealed partial class SimulationEngine
|
||||
return "none";
|
||||
}
|
||||
|
||||
var destinationEntryNode = ResolveSystemEntryNode(world, task.TargetSystemId);
|
||||
var destinationEntryPosition = destinationEntryNode?.Position ?? Vector3.Zero;
|
||||
return UpdateFtlTransit(ship, world, deltaSeconds, task.TargetSystemId, destinationEntryPosition, destinationEntryNode);
|
||||
var destinationEntryCelestial = ResolveSystemEntryCelestial(world, task.TargetSystemId);
|
||||
var destinationEntryPosition = destinationEntryCelestial?.Position ?? Vector3.Zero;
|
||||
return UpdateFtlTransit(ship, world, deltaSeconds, task.TargetSystemId, destinationEntryPosition, destinationEntryCelestial);
|
||||
}
|
||||
|
||||
var currentNode = ResolveCurrentNode(world, ship);
|
||||
if (targetNode is not null && currentNode is not null && !string.Equals(currentNode.Id, targetNode.Id, StringComparison.Ordinal))
|
||||
var currentCelestial = ResolveCurrentCelestial(world, ship);
|
||||
if (targetCelestial is not null && currentCelestial is not null && !string.Equals(currentCelestial.Id, targetCelestial.Id, StringComparison.Ordinal))
|
||||
{
|
||||
if (!HasShipCapabilities(ship.Definition, "warp"))
|
||||
{
|
||||
return UpdateLocalTravel(ship, world, deltaSeconds, task.TargetSystemId, targetPosition, targetNode, task.Threshold);
|
||||
return UpdateLocalTravel(ship, world, deltaSeconds, task.TargetSystemId, targetPosition, targetCelestial, task.Threshold);
|
||||
}
|
||||
|
||||
return UpdateWarpTransit(ship, world, deltaSeconds, targetPosition, targetNode);
|
||||
return UpdateWarpTransit(ship, world, deltaSeconds, targetPosition, targetCelestial);
|
||||
}
|
||||
|
||||
return UpdateLocalTravel(ship, world, deltaSeconds, task.TargetSystemId, targetPosition, targetNode, task.Threshold);
|
||||
return UpdateLocalTravel(ship, world, deltaSeconds, task.TargetSystemId, targetPosition, targetCelestial, task.Threshold);
|
||||
}
|
||||
|
||||
private static NodeRuntime? ResolveTravelTargetNode(SimulationWorld world, ControllerTaskRuntime task, Vector3 targetPosition)
|
||||
private static Vector3 ResolveCurrentTargetPosition(SimulationWorld world, ControllerTaskRuntime task)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(task.TargetEntityId))
|
||||
{
|
||||
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
|
||||
if (station?.NodeId is not null)
|
||||
if (station is not null)
|
||||
{
|
||||
return world.SpatialNodes.FirstOrDefault(candidate => candidate.Id == station.NodeId);
|
||||
return station.Position;
|
||||
}
|
||||
|
||||
var node = world.SpatialNodes.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
|
||||
if (node is not null)
|
||||
var celestial = world.Celestials.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
|
||||
if (celestial is not null)
|
||||
{
|
||||
return node;
|
||||
return celestial.Position;
|
||||
}
|
||||
}
|
||||
|
||||
return world.SpatialNodes
|
||||
return task.TargetPosition!.Value;
|
||||
}
|
||||
|
||||
private static CelestialRuntime? ResolveTravelTargetCelestial(SimulationWorld world, ControllerTaskRuntime task, Vector3 targetPosition)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(task.TargetEntityId))
|
||||
{
|
||||
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
|
||||
if (station?.CelestialId is not null)
|
||||
{
|
||||
return world.Celestials.FirstOrDefault(candidate => candidate.Id == station.CelestialId);
|
||||
}
|
||||
|
||||
var celestial = world.Celestials.FirstOrDefault(candidate => candidate.Id == task.TargetEntityId);
|
||||
if (celestial is not null)
|
||||
{
|
||||
return celestial;
|
||||
}
|
||||
}
|
||||
|
||||
return world.Celestials
|
||||
.Where(candidate => task.TargetSystemId is null || candidate.SystemId == task.TargetSystemId)
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(targetPosition))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static NodeRuntime? ResolveCurrentNode(SimulationWorld world, ShipRuntime ship)
|
||||
private static CelestialRuntime? ResolveCurrentCelestial(SimulationWorld world, ShipRuntime ship)
|
||||
{
|
||||
if (ship.SpatialState.CurrentNodeId is not null)
|
||||
if (ship.SpatialState.CurrentCelestialId is not null)
|
||||
{
|
||||
return world.SpatialNodes.FirstOrDefault(candidate => candidate.Id == ship.SpatialState.CurrentNodeId);
|
||||
return world.Celestials.FirstOrDefault(candidate => candidate.Id == ship.SpatialState.CurrentCelestialId);
|
||||
}
|
||||
|
||||
return world.SpatialNodes
|
||||
return world.Celestials
|
||||
.Where(candidate => candidate.SystemId == ship.SystemId)
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(ship.Position))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static NodeRuntime? ResolveSystemEntryNode(SimulationWorld world, string systemId) =>
|
||||
world.SpatialNodes.FirstOrDefault(candidate =>
|
||||
private static CelestialRuntime? ResolveSystemEntryCelestial(SimulationWorld world, string systemId) =>
|
||||
world.Celestials.FirstOrDefault(candidate =>
|
||||
candidate.SystemId == systemId &&
|
||||
candidate.Kind == SpatialNodeKind.Star);
|
||||
|
||||
@@ -129,14 +150,14 @@ public sealed partial class SimulationEngine
|
||||
float deltaSeconds,
|
||||
string targetSystemId,
|
||||
Vector3 targetPosition,
|
||||
NodeRuntime? targetNode,
|
||||
CelestialRuntime? targetCelestial,
|
||||
float threshold)
|
||||
{
|
||||
var distance = ship.Position.DistanceTo(targetPosition);
|
||||
ship.SpatialState.SpaceLayer = SpaceLayerKinds.LocalSpace;
|
||||
ship.SpatialState.MovementRegime = MovementRegimeKinds.LocalFlight;
|
||||
ship.SpatialState.Transit = null;
|
||||
ship.SpatialState.DestinationNodeId = targetNode?.Id;
|
||||
ship.SpatialState.DestinationNodeId = targetCelestial?.Id;
|
||||
|
||||
if (distance <= threshold)
|
||||
{
|
||||
@@ -144,8 +165,7 @@ public sealed partial class SimulationEngine
|
||||
ship.Position = targetPosition;
|
||||
ship.TargetPosition = ship.Position;
|
||||
ship.SystemId = targetSystemId;
|
||||
ship.SpatialState.CurrentNodeId = targetNode?.Id;
|
||||
ship.SpatialState.CurrentBubbleId = targetNode?.BubbleId;
|
||||
ship.SpatialState.CurrentCelestialId = targetCelestial?.Id;
|
||||
ship.State = ShipState.Arriving;
|
||||
return "arrived";
|
||||
}
|
||||
@@ -156,16 +176,16 @@ public sealed partial class SimulationEngine
|
||||
return "none";
|
||||
}
|
||||
|
||||
private string UpdateWarpTransit(ShipRuntime ship, SimulationWorld world, float deltaSeconds, Vector3 targetPosition, NodeRuntime targetNode)
|
||||
private string UpdateWarpTransit(ShipRuntime ship, SimulationWorld world, float deltaSeconds, Vector3 targetPosition, CelestialRuntime targetCelestial)
|
||||
{
|
||||
var transit = ship.SpatialState.Transit;
|
||||
if (transit is null || transit.Regime != MovementRegimeKinds.Warp || transit.DestinationNodeId != targetNode.Id)
|
||||
if (transit is null || transit.Regime != MovementRegimeKinds.Warp || transit.DestinationNodeId != targetCelestial.Id)
|
||||
{
|
||||
transit = new ShipTransitRuntime
|
||||
{
|
||||
Regime = MovementRegimeKinds.Warp,
|
||||
OriginNodeId = ship.SpatialState.CurrentNodeId,
|
||||
DestinationNodeId = targetNode.Id,
|
||||
OriginNodeId = ship.SpatialState.CurrentCelestialId,
|
||||
DestinationNodeId = targetCelestial.Id,
|
||||
StartedAtUtc = world.GeneratedAtUtc,
|
||||
};
|
||||
ship.SpatialState.Transit = transit;
|
||||
@@ -173,9 +193,8 @@ public sealed partial class SimulationEngine
|
||||
|
||||
ship.SpatialState.SpaceLayer = SpaceLayerKinds.SystemSpace;
|
||||
ship.SpatialState.MovementRegime = MovementRegimeKinds.Warp;
|
||||
ship.SpatialState.CurrentNodeId = null;
|
||||
ship.SpatialState.CurrentBubbleId = null;
|
||||
ship.SpatialState.DestinationNodeId = targetNode.Id;
|
||||
ship.SpatialState.CurrentCelestialId = null;
|
||||
ship.SpatialState.DestinationNodeId = targetCelestial.Id;
|
||||
|
||||
var spoolDuration = MathF.Max(0.4f, ship.Definition.SpoolTime * 0.5f);
|
||||
if (ship.State != ShipState.Warping)
|
||||
@@ -196,24 +215,24 @@ public sealed partial class SimulationEngine
|
||||
|
||||
var totalDistance = MathF.Max(0.001f, transit.OriginNodeId is null
|
||||
? ship.Position.DistanceTo(targetPosition)
|
||||
: (world.SpatialNodes.FirstOrDefault(candidate => candidate.Id == transit.OriginNodeId)?.Position.DistanceTo(targetPosition) ?? ship.Position.DistanceTo(targetPosition)));
|
||||
: (world.Celestials.FirstOrDefault(candidate => candidate.Id == transit.OriginNodeId)?.Position.DistanceTo(targetPosition) ?? ship.Position.DistanceTo(targetPosition)));
|
||||
ship.Position = ship.Position.MoveToward(targetPosition, GetWarpTravelSpeed(ship) * deltaSeconds);
|
||||
transit.Progress = MathF.Min(1f, 1f - (ship.Position.DistanceTo(targetPosition) / totalDistance));
|
||||
return ship.Position.DistanceTo(targetPosition) <= 18f
|
||||
? CompleteTransitArrival(ship, targetNode.SystemId, targetPosition, targetNode)
|
||||
? CompleteTransitArrival(ship, targetCelestial.SystemId, targetPosition, targetCelestial)
|
||||
: "none";
|
||||
}
|
||||
|
||||
private string UpdateFtlTransit(ShipRuntime ship, SimulationWorld world, float deltaSeconds, string targetSystemId, Vector3 targetPosition, NodeRuntime? targetNode)
|
||||
private string UpdateFtlTransit(ShipRuntime ship, SimulationWorld world, float deltaSeconds, string targetSystemId, Vector3 targetPosition, CelestialRuntime? targetCelestial)
|
||||
{
|
||||
var destinationNodeId = targetNode?.Id;
|
||||
var destinationNodeId = targetCelestial?.Id;
|
||||
var transit = ship.SpatialState.Transit;
|
||||
if (transit is null || transit.Regime != MovementRegimeKinds.FtlTransit || transit.DestinationNodeId != destinationNodeId)
|
||||
{
|
||||
transit = new ShipTransitRuntime
|
||||
{
|
||||
Regime = MovementRegimeKinds.FtlTransit,
|
||||
OriginNodeId = ship.SpatialState.CurrentNodeId,
|
||||
OriginNodeId = ship.SpatialState.CurrentCelestialId,
|
||||
DestinationNodeId = destinationNodeId,
|
||||
StartedAtUtc = world.GeneratedAtUtc,
|
||||
};
|
||||
@@ -222,8 +241,7 @@ public sealed partial class SimulationEngine
|
||||
|
||||
ship.SpatialState.SpaceLayer = SpaceLayerKinds.GalaxySpace;
|
||||
ship.SpatialState.MovementRegime = MovementRegimeKinds.FtlTransit;
|
||||
ship.SpatialState.CurrentNodeId = null;
|
||||
ship.SpatialState.CurrentBubbleId = null;
|
||||
ship.SpatialState.CurrentCelestialId = null;
|
||||
ship.SpatialState.DestinationNodeId = destinationNodeId;
|
||||
|
||||
if (ship.State != ShipState.Ftl)
|
||||
@@ -247,11 +265,11 @@ public sealed partial class SimulationEngine
|
||||
var totalDistance = MathF.Max(0.001f, originSystemPosition.DistanceTo(destinationSystemPosition));
|
||||
transit.Progress = MathF.Min(1f, transit.Progress + ((ship.Definition.FtlSpeed * deltaSeconds) / totalDistance));
|
||||
return transit.Progress >= 0.999f
|
||||
? CompleteSystemEntryArrival(ship, targetSystemId, targetPosition, targetNode)
|
||||
? CompleteSystemEntryArrival(ship, targetSystemId, targetPosition, targetCelestial)
|
||||
: "none";
|
||||
}
|
||||
|
||||
private static string CompleteTransitArrival(ShipRuntime ship, string targetSystemId, Vector3 targetPosition, NodeRuntime? targetNode)
|
||||
private static string CompleteTransitArrival(ShipRuntime ship, string targetSystemId, Vector3 targetPosition, CelestialRuntime? targetCelestial)
|
||||
{
|
||||
ship.ActionTimer = 0f;
|
||||
ship.Position = targetPosition;
|
||||
@@ -260,14 +278,13 @@ public sealed partial class SimulationEngine
|
||||
ship.SpatialState.Transit = null;
|
||||
ship.SpatialState.SpaceLayer = SpaceLayerKinds.LocalSpace;
|
||||
ship.SpatialState.MovementRegime = MovementRegimeKinds.LocalFlight;
|
||||
ship.SpatialState.CurrentNodeId = targetNode?.Id;
|
||||
ship.SpatialState.CurrentBubbleId = targetNode?.BubbleId;
|
||||
ship.SpatialState.DestinationNodeId = targetNode?.Id;
|
||||
ship.SpatialState.CurrentCelestialId = targetCelestial?.Id;
|
||||
ship.SpatialState.DestinationNodeId = targetCelestial?.Id;
|
||||
ship.State = ShipState.Arriving;
|
||||
return "arrived";
|
||||
}
|
||||
|
||||
private static string CompleteSystemEntryArrival(ShipRuntime ship, string targetSystemId, Vector3 targetPosition, NodeRuntime? targetNode)
|
||||
private static string CompleteSystemEntryArrival(ShipRuntime ship, string targetSystemId, Vector3 targetPosition, CelestialRuntime? targetCelestial)
|
||||
{
|
||||
ship.ActionTimer = 0f;
|
||||
ship.Position = targetPosition;
|
||||
@@ -276,9 +293,8 @@ public sealed partial class SimulationEngine
|
||||
ship.SpatialState.Transit = null;
|
||||
ship.SpatialState.SpaceLayer = SpaceLayerKinds.LocalSpace;
|
||||
ship.SpatialState.MovementRegime = MovementRegimeKinds.LocalFlight;
|
||||
ship.SpatialState.CurrentNodeId = targetNode?.Id;
|
||||
ship.SpatialState.CurrentBubbleId = targetNode?.BubbleId;
|
||||
ship.SpatialState.DestinationNodeId = targetNode?.Id;
|
||||
ship.SpatialState.CurrentCelestialId = targetCelestial?.Id;
|
||||
ship.SpatialState.DestinationNodeId = targetCelestial?.Id;
|
||||
ship.State = ShipState.Arriving;
|
||||
return "none";
|
||||
}
|
||||
|
||||
@@ -175,12 +175,12 @@ public sealed partial class SimulationEngine
|
||||
private void UpdateOrbitalState(SimulationWorld world)
|
||||
{
|
||||
var worldTimeSeconds = (float)world.OrbitalTimeSeconds;
|
||||
var spatialNodesById = world.SpatialNodes.ToDictionary(node => node.Id, StringComparer.Ordinal);
|
||||
var celestialsById = world.Celestials.ToDictionary(c => c.Id, StringComparer.Ordinal);
|
||||
|
||||
foreach (var system in world.Systems)
|
||||
{
|
||||
var starNodeId = $"node-{system.Definition.Id}-star";
|
||||
if (spatialNodesById.TryGetValue(starNodeId, out var starNode))
|
||||
if (celestialsById.TryGetValue(starNodeId, out var starNode))
|
||||
{
|
||||
starNode.Position = Vector3.Zero;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ public sealed partial class SimulationEngine
|
||||
{
|
||||
var planet = system.Definition.Planets[planetIndex];
|
||||
var planetNodeId = $"node-{system.Definition.Id}-planet-{planetIndex + 1}";
|
||||
if (!spatialNodesById.TryGetValue(planetNodeId, out var planetNode))
|
||||
if (!celestialsById.TryGetValue(planetNodeId, out var planetNode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -200,7 +200,7 @@ public sealed partial class SimulationEngine
|
||||
foreach (var lagrange in EnumeratePlanetLagrangePoints(planetPosition, planet))
|
||||
{
|
||||
var lagrangeId = $"node-{system.Definition.Id}-planet-{planetIndex + 1}-{lagrange.Designation.ToLowerInvariant()}";
|
||||
if (spatialNodesById.TryGetValue(lagrangeId, out var lagrangeNode))
|
||||
if (celestialsById.TryGetValue(lagrangeId, out var lagrangeNode))
|
||||
{
|
||||
lagrangeNode.Position = lagrange.Position;
|
||||
}
|
||||
@@ -209,7 +209,7 @@ public sealed partial class SimulationEngine
|
||||
for (var moonIndex = 0; moonIndex < planet.MoonCount; moonIndex += 1)
|
||||
{
|
||||
var moonId = $"node-{system.Definition.Id}-planet-{planetIndex + 1}-moon-{moonIndex + 1}";
|
||||
if (!spatialNodesById.TryGetValue(moonId, out var moonNode))
|
||||
if (!celestialsById.TryGetValue(moonId, out var moonNode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -221,30 +221,22 @@ public sealed partial class SimulationEngine
|
||||
|
||||
foreach (var station in world.Stations)
|
||||
{
|
||||
if (station.AnchorNodeId is null || !spatialNodesById.TryGetValue(station.AnchorNodeId, out var anchorNode))
|
||||
if (station.CelestialId is null || !celestialsById.TryGetValue(station.CelestialId, out var anchorCelestial))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
station.Position = anchorNode.Position;
|
||||
if (station.NodeId is not null && spatialNodesById.TryGetValue(station.NodeId, out var stationNode))
|
||||
{
|
||||
stationNode.Position = station.Position;
|
||||
}
|
||||
station.Position = anchorCelestial.Position;
|
||||
}
|
||||
|
||||
foreach (var node in world.Nodes)
|
||||
{
|
||||
if (node.AnchorNodeId is null || !spatialNodesById.TryGetValue(node.AnchorNodeId, out var anchorNode))
|
||||
if (node.CelestialId is null || !celestialsById.TryGetValue(node.CelestialId, out var anchorCelestial))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
node.Position = Add(anchorNode.Position, ComputeResourceNodeOffset(node, worldTimeSeconds));
|
||||
if (spatialNodesById.TryGetValue(node.Id, out var resourceNode))
|
||||
{
|
||||
resourceNode.Position = node.Position;
|
||||
}
|
||||
node.Position = Add(anchorCelestial.Position, ComputeResourceNodeOffset(node, worldTimeSeconds));
|
||||
}
|
||||
|
||||
foreach (var ship in world.Ships.Where(ship => ship.DockedStationId is not null))
|
||||
@@ -263,11 +255,6 @@ public sealed partial class SimulationEngine
|
||||
|
||||
private static void SyncSpatialState(SimulationWorld world)
|
||||
{
|
||||
foreach (var bubble in world.LocalBubbles)
|
||||
{
|
||||
bubble.OccupantShipIds.Clear();
|
||||
}
|
||||
|
||||
foreach (var ship in world.Ships)
|
||||
{
|
||||
ship.SpatialState.CurrentSystemId = ship.SystemId;
|
||||
@@ -275,25 +262,17 @@ public sealed partial class SimulationEngine
|
||||
ship.SpatialState.SystemPosition = ship.Position;
|
||||
if (ship.SpatialState.Transit is not null)
|
||||
{
|
||||
ship.SpatialState.CurrentNodeId = null;
|
||||
ship.SpatialState.CurrentBubbleId = null;
|
||||
ship.SpatialState.CurrentCelestialId = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
ship.SpatialState.SpaceLayer = SpaceLayerKinds.LocalSpace;
|
||||
ship.SpatialState.MovementRegime = MovementRegimeKinds.LocalFlight;
|
||||
var nearestNode = world.SpatialNodes
|
||||
var nearestCelestial = world.Celestials
|
||||
.Where(candidate => candidate.SystemId == ship.SystemId)
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(ship.Position))
|
||||
.FirstOrDefault();
|
||||
ship.SpatialState.CurrentNodeId = nearestNode?.Id;
|
||||
ship.SpatialState.CurrentBubbleId = nearestNode?.BubbleId;
|
||||
|
||||
if (nearestNode is not null)
|
||||
{
|
||||
var nearestBubble = world.LocalBubbles.FirstOrDefault(candidate => candidate.Id == nearestNode.BubbleId);
|
||||
nearestBubble?.OccupantShipIds.Add(ship.Id);
|
||||
}
|
||||
ship.SpatialState.CurrentCelestialId = nearestCelestial?.Id;
|
||||
|
||||
if (ship.DockedStationId is null)
|
||||
{
|
||||
@@ -301,15 +280,10 @@ public sealed partial class SimulationEngine
|
||||
}
|
||||
|
||||
var station = world.Stations.FirstOrDefault(candidate => candidate.Id == ship.DockedStationId);
|
||||
if (station?.BubbleId is null)
|
||||
if (station?.CelestialId is not null)
|
||||
{
|
||||
continue;
|
||||
ship.SpatialState.CurrentCelestialId = station.CelestialId;
|
||||
}
|
||||
|
||||
ship.SpatialState.CurrentNodeId = station.NodeId;
|
||||
ship.SpatialState.CurrentBubbleId = station.BubbleId;
|
||||
var bubble = world.LocalBubbles.FirstOrDefault(candidate => candidate.Id == station.BubbleId);
|
||||
bubble?.OccupantShipIds.Add(ship.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,29 +39,20 @@ public sealed partial class SimulationEngine
|
||||
planet.Size,
|
||||
planet.Color,
|
||||
planet.HasRing)).ToList())).ToList(),
|
||||
world.SpatialNodes.Select(ToSpatialNodeDelta).Select(node => new SpatialNodeSnapshot(
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
node.Kind,
|
||||
node.LocalPosition,
|
||||
node.BubbleId,
|
||||
node.ParentNodeId,
|
||||
node.OccupyingStructureId,
|
||||
node.OrbitReferenceId)).ToList(),
|
||||
world.LocalBubbles.Select(ToLocalBubbleDelta).Select(bubble => new LocalBubbleSnapshot(
|
||||
bubble.Id,
|
||||
bubble.NodeId,
|
||||
bubble.SystemId,
|
||||
bubble.Radius,
|
||||
bubble.OccupantShipIds,
|
||||
bubble.OccupantStationIds,
|
||||
bubble.OccupantClaimIds,
|
||||
bubble.OccupantConstructionSiteIds)).ToList(),
|
||||
world.Celestials.Select(ToCelestialDelta).Select(c => new CelestialSnapshot(
|
||||
c.Id,
|
||||
c.SystemId,
|
||||
c.Kind,
|
||||
c.OrbitalAnchor,
|
||||
c.LocalSpaceRadius,
|
||||
c.ParentNodeId,
|
||||
c.OccupyingStructureId,
|
||||
c.OrbitReferenceId)).ToList(),
|
||||
world.Nodes.Select(ToNodeDelta).Select(node => new ResourceNodeSnapshot(
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
node.LocalPosition,
|
||||
node.AnchorNodeId,
|
||||
node.CelestialId,
|
||||
node.SourceKind,
|
||||
node.OreRemaining,
|
||||
node.MaxOre,
|
||||
@@ -72,9 +63,7 @@ public sealed partial class SimulationEngine
|
||||
station.Category,
|
||||
station.SystemId,
|
||||
station.LocalPosition,
|
||||
station.NodeId,
|
||||
station.BubbleId,
|
||||
station.AnchorNodeId,
|
||||
station.CelestialId,
|
||||
station.Color,
|
||||
station.DockedShips,
|
||||
station.DockedShipIds,
|
||||
@@ -95,8 +84,7 @@ public sealed partial class SimulationEngine
|
||||
claim.Id,
|
||||
claim.FactionId,
|
||||
claim.SystemId,
|
||||
claim.NodeId,
|
||||
claim.BubbleId,
|
||||
claim.CelestialId,
|
||||
claim.State,
|
||||
claim.Health,
|
||||
claim.PlacedAtUtc,
|
||||
@@ -105,8 +93,7 @@ public sealed partial class SimulationEngine
|
||||
site.Id,
|
||||
site.FactionId,
|
||||
site.SystemId,
|
||||
site.NodeId,
|
||||
site.BubbleId,
|
||||
site.CelestialId,
|
||||
site.TargetKind,
|
||||
site.TargetDefinitionId,
|
||||
site.BlueprintId,
|
||||
@@ -155,8 +142,7 @@ public sealed partial class SimulationEngine
|
||||
ship.BehaviorPhase,
|
||||
ship.ControllerTaskKind,
|
||||
ship.CommanderObjective,
|
||||
ship.NodeId,
|
||||
ship.BubbleId,
|
||||
ship.CelestialId,
|
||||
ship.DockedStationId,
|
||||
ship.CommanderId,
|
||||
ship.PolicySetId,
|
||||
@@ -191,14 +177,9 @@ public sealed partial class SimulationEngine
|
||||
node.LastDeltaSignature = BuildNodeSignature(node);
|
||||
}
|
||||
|
||||
foreach (var node in world.SpatialNodes)
|
||||
foreach (var celestial in world.Celestials)
|
||||
{
|
||||
node.LastDeltaSignature = BuildSpatialNodeSignature(node);
|
||||
}
|
||||
|
||||
foreach (var bubble in world.LocalBubbles)
|
||||
{
|
||||
bubble.LastDeltaSignature = BuildLocalBubbleSignature(bubble);
|
||||
celestial.LastDeltaSignature = BuildCelestialSignature(celestial);
|
||||
}
|
||||
|
||||
foreach (var station in world.Stations)
|
||||
@@ -255,37 +236,19 @@ public sealed partial class SimulationEngine
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<SpatialNodeDelta> BuildSpatialNodeDeltas(SimulationWorld world)
|
||||
private static IReadOnlyList<CelestialDelta> BuildCelestialDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<SpatialNodeDelta>();
|
||||
foreach (var node in world.SpatialNodes)
|
||||
var deltas = new List<CelestialDelta>();
|
||||
foreach (var celestial in world.Celestials)
|
||||
{
|
||||
var signature = BuildSpatialNodeSignature(node);
|
||||
if (signature == node.LastDeltaSignature)
|
||||
var signature = BuildCelestialSignature(celestial);
|
||||
if (signature == celestial.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
node.LastDeltaSignature = signature;
|
||||
deltas.Add(ToSpatialNodeDelta(node));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<LocalBubbleDelta> BuildLocalBubbleDeltas(SimulationWorld world)
|
||||
{
|
||||
var deltas = new List<LocalBubbleDelta>();
|
||||
foreach (var bubble in world.LocalBubbles)
|
||||
{
|
||||
var signature = BuildLocalBubbleSignature(bubble);
|
||||
if (signature == bubble.LastDeltaSignature)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bubble.LastDeltaSignature = signature;
|
||||
deltas.Add(ToLocalBubbleDelta(bubble));
|
||||
celestial.LastDeltaSignature = signature;
|
||||
deltas.Add(ToCelestialDelta(celestial));
|
||||
}
|
||||
|
||||
return deltas;
|
||||
@@ -424,22 +387,17 @@ public sealed partial class SimulationEngine
|
||||
string.Equals(c.Kind, CommanderKind.Faction, StringComparison.Ordinal));
|
||||
|
||||
private static string BuildNodeSignature(ResourceNodeRuntime node) =>
|
||||
$"{node.SystemId}|{node.Position.X:0.###}|{node.Position.Y:0.###}|{node.Position.Z:0.###}|{node.AnchorNodeId}|{node.OreRemaining:0.###}";
|
||||
$"{node.SystemId}|{node.Position.X:0.###}|{node.Position.Y:0.###}|{node.Position.Z:0.###}|{node.CelestialId}|{node.OreRemaining:0.###}";
|
||||
|
||||
private static string BuildSpatialNodeSignature(NodeRuntime node) =>
|
||||
$"{node.SystemId}|{node.Kind.ToContractValue()}|{node.Position.X:0.###}|{node.Position.Y:0.###}|{node.Position.Z:0.###}|{node.BubbleId}|{node.ParentNodeId}|{node.OccupyingStructureId}|{node.OrbitReferenceId}";
|
||||
|
||||
private static string BuildLocalBubbleSignature(LocalBubbleRuntime bubble) =>
|
||||
$"{bubble.SystemId}|{bubble.NodeId}|{bubble.Radius:0.###}|{string.Join(",", bubble.OccupantShipIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", bubble.OccupantStationIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", bubble.OccupantClaimIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", bubble.OccupantConstructionSiteIds.OrderBy(id => id, StringComparer.Ordinal))}";
|
||||
private static string BuildCelestialSignature(CelestialRuntime celestial) =>
|
||||
$"{celestial.SystemId}|{celestial.Kind.ToContractValue()}|{celestial.Position.X:0.###}|{celestial.Position.Y:0.###}|{celestial.Position.Z:0.###}|{celestial.LocalSpaceRadius:0.###}|{celestial.ParentNodeId}|{celestial.OccupyingStructureId}|{celestial.OrbitReferenceId}";
|
||||
|
||||
private static string BuildStationSignature(SimulationWorld world, StationRuntime station)
|
||||
{
|
||||
var processes = ToStationActionProgressSnapshots(world, station);
|
||||
return string.Join("|",
|
||||
station.SystemId,
|
||||
station.NodeId ?? "none",
|
||||
station.BubbleId ?? "none",
|
||||
station.AnchorNodeId ?? "none",
|
||||
station.CelestialId ?? "none",
|
||||
station.CommanderId ?? "none",
|
||||
station.PolicySetId ?? "none",
|
||||
BuildInventorySignature(station.Inventory),
|
||||
@@ -458,10 +416,10 @@ public sealed partial class SimulationEngine
|
||||
}
|
||||
|
||||
private static string BuildClaimSignature(ClaimRuntime claim) =>
|
||||
$"{claim.FactionId}|{claim.SystemId}|{claim.NodeId}|{claim.BubbleId}|{claim.State}|{claim.Health:0.###}|{claim.ActivatesAtUtc:O}";
|
||||
$"{claim.FactionId}|{claim.SystemId}|{claim.CelestialId}|{claim.State}|{claim.Health:0.###}|{claim.ActivatesAtUtc:O}";
|
||||
|
||||
private static string BuildConstructionSiteSignature(ConstructionSiteRuntime site) =>
|
||||
$"{site.FactionId}|{site.SystemId}|{site.NodeId}|{site.BubbleId}|{site.TargetKind}|{site.TargetDefinitionId}|{site.BlueprintId}|{site.ClaimId}|{site.StationId}|{site.State}|{site.Progress:0.###}|{BuildInventorySignature(site.Inventory)}|{BuildInventorySignature(site.RequiredItems)}|{BuildInventorySignature(site.DeliveredItems)}|{string.Join(",", site.AssignedConstructorShipIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", site.MarketOrderIds.OrderBy(id => id, StringComparer.Ordinal))}";
|
||||
$"{site.FactionId}|{site.SystemId}|{site.CelestialId}|{site.TargetKind}|{site.TargetDefinitionId}|{site.BlueprintId}|{site.ClaimId}|{site.StationId}|{site.State}|{site.Progress:0.###}|{BuildInventorySignature(site.Inventory)}|{BuildInventorySignature(site.RequiredItems)}|{BuildInventorySignature(site.DeliveredItems)}|{string.Join(",", site.AssignedConstructorShipIds.OrderBy(id => id, StringComparer.Ordinal))}|{string.Join(",", site.MarketOrderIds.OrderBy(id => id, StringComparer.Ordinal))}";
|
||||
|
||||
private static string BuildMarketOrderSignature(MarketOrderRuntime order) =>
|
||||
$"{order.FactionId}|{order.StationId}|{order.ConstructionSiteId}|{order.Kind}|{order.ItemId}|{order.Amount:0.###}|{order.RemainingAmount:0.###}|{order.Valuation:0.###}|{order.ReserveThreshold?.ToString("0.###") ?? "none"}|{order.PolicySetId}|{order.State}";
|
||||
@@ -486,14 +444,12 @@ public sealed partial class SimulationEngine
|
||||
ship.DefaultBehavior.Kind,
|
||||
ship.DefaultBehavior.Phase ?? "none",
|
||||
ship.ControllerTask.Kind.ToContractValue(),
|
||||
ship.SpatialState.CurrentNodeId ?? "none",
|
||||
ship.SpatialState.CurrentBubbleId ?? "none",
|
||||
ship.SpatialState.CurrentCelestialId ?? "none",
|
||||
ship.DockedStationId ?? "none",
|
||||
ship.CommanderId ?? "none",
|
||||
ship.PolicySetId ?? "none",
|
||||
ship.SpatialState.SpaceLayer,
|
||||
ship.SpatialState.CurrentNodeId ?? "none",
|
||||
ship.SpatialState.CurrentBubbleId ?? "none",
|
||||
ship.SpatialState.CurrentCelestialId ?? "none",
|
||||
ship.SpatialState.MovementRegime,
|
||||
ship.SpatialState.DestinationNodeId ?? "none",
|
||||
ship.SpatialState.Transit?.Regime ?? "none",
|
||||
@@ -528,31 +484,21 @@ public sealed partial class SimulationEngine
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
ToDto(node.Position),
|
||||
node.AnchorNodeId,
|
||||
node.CelestialId,
|
||||
node.SourceKind,
|
||||
node.OreRemaining,
|
||||
node.MaxOre,
|
||||
node.ItemId);
|
||||
|
||||
private static SpatialNodeDelta ToSpatialNodeDelta(NodeRuntime node) => new(
|
||||
node.Id,
|
||||
node.SystemId,
|
||||
node.Kind.ToContractValue(),
|
||||
ToDto(node.Position),
|
||||
node.BubbleId,
|
||||
node.ParentNodeId,
|
||||
node.OccupyingStructureId,
|
||||
node.OrbitReferenceId);
|
||||
|
||||
private static LocalBubbleDelta ToLocalBubbleDelta(LocalBubbleRuntime bubble) => new(
|
||||
bubble.Id,
|
||||
bubble.NodeId,
|
||||
bubble.SystemId,
|
||||
bubble.Radius,
|
||||
bubble.OccupantShipIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
bubble.OccupantStationIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
bubble.OccupantClaimIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
bubble.OccupantConstructionSiteIds.OrderBy(id => id, StringComparer.Ordinal).ToList());
|
||||
private static CelestialDelta ToCelestialDelta(CelestialRuntime celestial) => new(
|
||||
celestial.Id,
|
||||
celestial.SystemId,
|
||||
celestial.Kind.ToContractValue(),
|
||||
ToDto(celestial.Position),
|
||||
celestial.LocalSpaceRadius,
|
||||
celestial.ParentNodeId,
|
||||
celestial.OccupyingStructureId,
|
||||
celestial.OrbitReferenceId);
|
||||
|
||||
private static StationDelta ToStationDelta(SimulationWorld world, StationRuntime station) => new(
|
||||
station.Id,
|
||||
@@ -560,9 +506,7 @@ public sealed partial class SimulationEngine
|
||||
station.Category,
|
||||
station.SystemId,
|
||||
ToDto(station.Position),
|
||||
station.NodeId,
|
||||
station.BubbleId,
|
||||
station.AnchorNodeId,
|
||||
station.CelestialId,
|
||||
station.Color,
|
||||
station.DockedShipIds.Count,
|
||||
station.DockedShipIds.OrderBy(id => id, StringComparer.Ordinal).ToList(),
|
||||
@@ -615,8 +559,7 @@ public sealed partial class SimulationEngine
|
||||
claim.Id,
|
||||
claim.FactionId,
|
||||
claim.SystemId,
|
||||
claim.NodeId,
|
||||
claim.BubbleId,
|
||||
claim.CelestialId,
|
||||
claim.State,
|
||||
claim.Health,
|
||||
claim.PlacedAtUtc,
|
||||
@@ -626,8 +569,7 @@ public sealed partial class SimulationEngine
|
||||
site.Id,
|
||||
site.FactionId,
|
||||
site.SystemId,
|
||||
site.NodeId,
|
||||
site.BubbleId,
|
||||
site.CelestialId,
|
||||
site.TargetKind,
|
||||
site.TargetDefinitionId,
|
||||
site.BlueprintId,
|
||||
@@ -696,8 +638,7 @@ public sealed partial class SimulationEngine
|
||||
ship.DefaultBehavior.Phase,
|
||||
ship.ControllerTask.Kind.ToContractValue(),
|
||||
commander?.ActiveActionName,
|
||||
ship.SpatialState.CurrentNodeId,
|
||||
ship.SpatialState.CurrentBubbleId,
|
||||
ship.SpatialState.CurrentCelestialId,
|
||||
ship.DockedStationId,
|
||||
ship.CommanderId,
|
||||
ship.PolicySetId,
|
||||
@@ -810,8 +751,7 @@ public sealed partial class SimulationEngine
|
||||
private static ShipSpatialStateSnapshot ToShipSpatialStateSnapshot(ShipSpatialStateRuntime state) => new(
|
||||
state.SpaceLayer,
|
||||
state.CurrentSystemId,
|
||||
state.CurrentNodeId,
|
||||
state.CurrentBubbleId,
|
||||
state.CurrentCelestialId,
|
||||
state.LocalPosition is null ? null : ToDto(state.LocalPosition.Value),
|
||||
state.SystemPosition is null ? null : ToDto(state.SystemPosition.Value),
|
||||
state.MovementRegime,
|
||||
|
||||
@@ -305,7 +305,7 @@ public sealed partial class SimulationEngine
|
||||
|
||||
private static bool FactionControlsSystem(SimulationWorld world, string factionId, string systemId)
|
||||
{
|
||||
var totalLagrangePoints = world.SpatialNodes.Count(node =>
|
||||
var totalLagrangePoints = world.Celestials.Count(node =>
|
||||
node.SystemId == systemId &&
|
||||
node.Kind == SpatialNodeKind.LagrangePoint);
|
||||
if (totalLagrangePoints == 0)
|
||||
|
||||
@@ -90,8 +90,7 @@ public sealed partial class SimulationEngine
|
||||
{
|
||||
CurrentSystemId = station.SystemId,
|
||||
SpaceLayer = SpaceLayerKinds.LocalSpace,
|
||||
CurrentNodeId = station.NodeId,
|
||||
CurrentBubbleId = station.BubbleId,
|
||||
CurrentCelestialId = station.CelestialId,
|
||||
LocalPosition = position,
|
||||
SystemPosition = position,
|
||||
MovementRegime = MovementRegimeKinds.LocalFlight,
|
||||
|
||||
@@ -70,8 +70,7 @@ public sealed partial class SimulationEngine
|
||||
world.GeneratedAtUtc,
|
||||
false,
|
||||
events,
|
||||
BuildSpatialNodeDeltas(world),
|
||||
BuildLocalBubbleDeltas(world),
|
||||
BuildCelestialDeltas(world),
|
||||
BuildNodeDeltas(world),
|
||||
BuildStationDeltas(world),
|
||||
BuildClaimDeltas(world),
|
||||
|
||||
@@ -116,7 +116,6 @@ public sealed class WorldService(
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[]);
|
||||
|
||||
_history.Enqueue(resetDelta);
|
||||
@@ -132,8 +131,7 @@ public sealed class WorldService(
|
||||
private static bool HasMeaningfulDelta(WorldDelta delta) =>
|
||||
delta.RequiresSnapshotRefresh
|
||||
|| delta.Events.Count > 0
|
||||
|| delta.SpatialNodes.Count > 0
|
||||
|| delta.LocalBubbles.Count > 0
|
||||
|| delta.Celestials.Count > 0
|
||||
|| delta.Nodes.Count > 0
|
||||
|| delta.Stations.Count > 0
|
||||
|| delta.Claims.Count > 0
|
||||
@@ -168,9 +166,9 @@ public sealed class WorldService(
|
||||
}
|
||||
|
||||
var systemFilter = scope.SystemId;
|
||||
if (string.Equals(scope.ScopeKind, "local-bubble", StringComparison.OrdinalIgnoreCase) && systemFilter is null && scope.BubbleId is not null)
|
||||
if (string.Equals(scope.ScopeKind, "local-celestial", StringComparison.OrdinalIgnoreCase) && systemFilter is null && scope.CelestialId is not null)
|
||||
{
|
||||
systemFilter = ResolveBubbleSystemId(scope.BubbleId);
|
||||
systemFilter = ResolveCelestialSystemId(scope.CelestialId);
|
||||
}
|
||||
|
||||
return delta with
|
||||
@@ -179,8 +177,7 @@ public sealed class WorldService(
|
||||
.Select((evt) => EnrichEventScope(evt))
|
||||
.Where((evt) => IsEventVisibleToScope(evt, scope, systemFilter))
|
||||
.ToList(),
|
||||
SpatialNodes = delta.SpatialNodes.Where((node) => systemFilter is null || node.SystemId == systemFilter).ToList(),
|
||||
LocalBubbles = delta.LocalBubbles.Where((bubble) => systemFilter is null || bubble.SystemId == systemFilter).ToList(),
|
||||
Celestials = delta.Celestials.Where((celestial) => systemFilter is null || celestial.SystemId == systemFilter).ToList(),
|
||||
Nodes = delta.Nodes.Where((node) => systemFilter is null || node.SystemId == systemFilter).ToList(),
|
||||
Stations = delta.Stations.Where((station) => systemFilter is null || station.SystemId == systemFilter).ToList(),
|
||||
Claims = delta.Claims.Where((claim) => systemFilter is null || claim.SystemId == systemFilter).ToList(),
|
||||
@@ -205,8 +202,7 @@ public sealed class WorldService(
|
||||
"ship" => WithEntityScope(evt, "system", _world.Ships.FirstOrDefault((ship) => ship.Id == evt.EntityId)?.SystemId),
|
||||
"station" => WithEntityScope(evt, "system", _world.Stations.FirstOrDefault((station) => station.Id == evt.EntityId)?.SystemId),
|
||||
"node" => WithEntityScope(evt, "system", _world.Nodes.FirstOrDefault((node) => node.Id == evt.EntityId)?.SystemId),
|
||||
"spatial-node" => WithEntityScope(evt, "system", _world.SpatialNodes.FirstOrDefault((node) => node.Id == evt.EntityId)?.SystemId),
|
||||
"local-bubble" => WithEntityScope(evt, "local-bubble", _world.LocalBubbles.FirstOrDefault((bubble) => bubble.Id == evt.EntityId)?.Id),
|
||||
"celestial" => WithEntityScope(evt, "system", _world.Celestials.FirstOrDefault((c) => c.Id == evt.EntityId)?.SystemId),
|
||||
"claim" => WithEntityScope(evt, "system", _world.Claims.FirstOrDefault((claim) => claim.Id == evt.EntityId)?.SystemId),
|
||||
"construction-site" => WithEntityScope(evt, "system", _world.ConstructionSites.FirstOrDefault((site) => site.Id == evt.EntityId)?.SystemId),
|
||||
"market-order" => WithEntityScope(evt, "system", ResolveMarketOrderSystemId(evt.EntityId)),
|
||||
@@ -226,8 +222,8 @@ public sealed class WorldService(
|
||||
ScopeEntityId = scopeEntityId,
|
||||
};
|
||||
|
||||
private string? ResolveBubbleSystemId(string bubbleId) =>
|
||||
_world.LocalBubbles.FirstOrDefault((bubble) => bubble.Id == bubbleId)?.SystemId;
|
||||
private string? ResolveCelestialSystemId(string celestialId) =>
|
||||
_world.Celestials.FirstOrDefault((c) => c.Id == celestialId)?.SystemId;
|
||||
|
||||
private string? ResolveMarketOrderSystemId(string orderId)
|
||||
{
|
||||
@@ -271,7 +267,7 @@ public sealed class WorldService(
|
||||
{
|
||||
"universe" => true,
|
||||
"system" => evt.ScopeKind == "universe" || evt.ScopeEntityId == systemFilter,
|
||||
"local-bubble" => evt.ScopeKind == "universe" || evt.ScopeEntityId == systemFilter,
|
||||
"local-celestial" => evt.ScopeKind == "universe" || evt.ScopeEntityId == systemFilter,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user