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