Complete universe model migration
This commit is contained in:
@@ -9,6 +9,11 @@ public sealed partial class ShipAiService
|
||||
{
|
||||
private static Vector3 ResolveCurrentTargetPosition(SimulationWorld world, ShipSubTaskRuntime subTask)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(subTask.TargetAnchorId) && ResolveAnchor(world, subTask.TargetAnchorId) is not null)
|
||||
{
|
||||
return subTask.TargetPosition ?? Vector3.Zero;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(subTask.TargetEntityId))
|
||||
{
|
||||
var ship = world.Ships.FirstOrDefault(candidate => candidate.Id == subTask.TargetEntityId);
|
||||
@@ -44,15 +49,20 @@ public sealed partial class ShipAiService
|
||||
if (!string.IsNullOrWhiteSpace(subTask.TargetEntityId))
|
||||
{
|
||||
var station = ResolveStation(world, subTask.TargetEntityId);
|
||||
if (station?.CelestialId is not null)
|
||||
if (station?.AnchorId is not null)
|
||||
{
|
||||
return world.Celestials.FirstOrDefault(candidate => candidate.Id == station.CelestialId);
|
||||
return ResolveAnchorBackedCelestial(world, station.AnchorId);
|
||||
}
|
||||
|
||||
var site = world.ConstructionSites.FirstOrDefault(candidate => candidate.Id == subTask.TargetEntityId);
|
||||
if (site?.CelestialId is not null)
|
||||
if (site?.AnchorId is not null)
|
||||
{
|
||||
return world.Celestials.FirstOrDefault(candidate => candidate.Id == site.CelestialId);
|
||||
return ResolveAnchorBackedCelestial(world, site.AnchorId);
|
||||
}
|
||||
|
||||
if (ResolveAnchor(world, subTask.TargetEntityId) is { } anchorBackedCelestialTarget)
|
||||
{
|
||||
return ResolveAnchorBackedCelestial(world, anchorBackedCelestialTarget.Id);
|
||||
}
|
||||
|
||||
var celestial = world.Celestials.FirstOrDefault(candidate => candidate.Id == subTask.TargetEntityId);
|
||||
@@ -76,25 +86,145 @@ public sealed partial class ShipAiService
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static AnchorRuntime? ResolveTravelTargetAnchor(SimulationWorld world, ShipSubTaskRuntime subTask, Vector3 targetPosition)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(subTask.TargetAnchorId) && ResolveAnchor(world, subTask.TargetAnchorId) is { } explicitTargetAnchor)
|
||||
{
|
||||
return explicitTargetAnchor;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(subTask.TargetEntityId))
|
||||
{
|
||||
var station = ResolveStation(world, subTask.TargetEntityId);
|
||||
if (station?.AnchorId is not null)
|
||||
{
|
||||
return ResolveAnchor(world, station.AnchorId);
|
||||
}
|
||||
|
||||
var site = world.ConstructionSites.FirstOrDefault(candidate => candidate.Id == subTask.TargetEntityId);
|
||||
if (site?.AnchorId is not null)
|
||||
{
|
||||
return ResolveAnchor(world, site.AnchorId);
|
||||
}
|
||||
|
||||
var node = ResolveNode(world, subTask.TargetEntityId);
|
||||
if (node is not null)
|
||||
{
|
||||
return ResolveAnchor(world, node.AnchorId);
|
||||
}
|
||||
|
||||
if (ResolveAnchor(world, subTask.TargetEntityId) is { } directAnchor)
|
||||
{
|
||||
return directAnchor;
|
||||
}
|
||||
|
||||
if (world.Celestials.FirstOrDefault(candidate => candidate.Id == subTask.TargetEntityId) is { } celestial)
|
||||
{
|
||||
return ResolveAnchor(world, celestial.Id);
|
||||
}
|
||||
|
||||
if (world.Wrecks.FirstOrDefault(candidate => candidate.Id == subTask.TargetEntityId) is { } wreck)
|
||||
{
|
||||
return world.Anchors
|
||||
.Where(candidate => candidate.SystemId == wreck.SystemId)
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(wreck.Position))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
return world.Anchors
|
||||
.Where(candidate => subTask.TargetSystemId is null || candidate.SystemId == subTask.TargetSystemId)
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(targetPosition))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static AnchorRuntime? ResolveCurrentAnchor(SimulationWorld world, ShipRuntime ship)
|
||||
{
|
||||
if (ship.SpatialState.CurrentAnchorId is not null && ResolveAnchor(world, ship.SpatialState.CurrentAnchorId) is { } explicitAnchor)
|
||||
{
|
||||
return explicitAnchor;
|
||||
}
|
||||
|
||||
if (ship.DockedStationId is not null && ResolveStation(world, ship.DockedStationId)?.AnchorId is { } dockAnchorId)
|
||||
{
|
||||
return ResolveAnchor(world, dockAnchorId);
|
||||
}
|
||||
|
||||
return world.Anchors
|
||||
.Where(candidate => candidate.SystemId == ship.SystemId)
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(ResolveShipSystemPosition(world, ship)))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static CelestialRuntime? ResolveCurrentCelestial(SimulationWorld world, ShipRuntime ship)
|
||||
{
|
||||
if (ship.SpatialState.CurrentCelestialId is not null)
|
||||
if (ship.SpatialState.CurrentAnchorId is not null && ResolveAnchorBackedCelestial(world, ship.SpatialState.CurrentAnchorId) is { } currentAnchorCelestial)
|
||||
{
|
||||
return world.Celestials.FirstOrDefault(candidate => candidate.Id == ship.SpatialState.CurrentCelestialId);
|
||||
return currentAnchorCelestial;
|
||||
}
|
||||
|
||||
return world.Celestials
|
||||
.Where(candidate => candidate.SystemId == ship.SystemId)
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(ship.Position))
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(ResolveShipSystemPosition(world, ship)))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static CelestialRuntime? ResolveSystemEntryCelestial(SimulationWorld world, string systemId) =>
|
||||
world.Celestials.FirstOrDefault(candidate => candidate.SystemId == systemId && candidate.Kind == SpatialNodeKind.Star);
|
||||
|
||||
private static AnchorRuntime? ResolveSystemEntryAnchor(SimulationWorld world, string systemId) =>
|
||||
world.Anchors.FirstOrDefault(candidate => candidate.SystemId == systemId && candidate.Kind == SpatialNodeKind.Star);
|
||||
|
||||
private static Vector3 ResolveSystemGalaxyPosition(SimulationWorld world, string systemId) =>
|
||||
world.Systems.FirstOrDefault(candidate => candidate.Definition.Id == systemId)?.Position ?? Vector3.Zero;
|
||||
|
||||
private static Vector3 ResolveAnchorPosition(SimulationWorld world, string? anchorId, Vector3 fallbackPosition) =>
|
||||
ResolveAnchor(world, anchorId)?.Position ?? fallbackPosition;
|
||||
|
||||
private static Vector3 ResolveStationSystemPosition(SimulationWorld world, StationRuntime station)
|
||||
{
|
||||
if (station.AnchorId is not null && ResolveAnchor(world, station.AnchorId) is { } anchor)
|
||||
{
|
||||
return new Vector3(
|
||||
anchor.Position.X + station.Position.X,
|
||||
anchor.Position.Y + station.Position.Y,
|
||||
anchor.Position.Z + station.Position.Z);
|
||||
}
|
||||
|
||||
return station.Position;
|
||||
}
|
||||
|
||||
private static Vector3 ResolveNodeSystemPosition(SimulationWorld world, ResourceNodeRuntime node)
|
||||
{
|
||||
if (ResolveAnchor(world, node.AnchorId) is { } anchor)
|
||||
{
|
||||
return new Vector3(
|
||||
anchor.Position.X + node.Position.X,
|
||||
anchor.Position.Y + node.Position.Y,
|
||||
anchor.Position.Z + node.Position.Z);
|
||||
}
|
||||
|
||||
return node.Position;
|
||||
}
|
||||
|
||||
private static Vector3 ResolveShipSystemPosition(SimulationWorld world, ShipRuntime ship)
|
||||
{
|
||||
if (ship.SpatialState.SystemPosition is { } systemPosition)
|
||||
{
|
||||
return systemPosition;
|
||||
}
|
||||
|
||||
if (ResolveCurrentAnchor(world, ship) is { } anchor)
|
||||
{
|
||||
return new Vector3(
|
||||
anchor.Position.X + ship.Position.X,
|
||||
anchor.Position.Y + ship.Position.Y,
|
||||
anchor.Position.Z + ship.Position.Z);
|
||||
}
|
||||
|
||||
return ship.Position;
|
||||
}
|
||||
|
||||
private static float GetLocalTravelSpeed(ShipRuntime ship) =>
|
||||
SimulationUnits.MetersPerSecondToKilometersPerSecond(ship.Definition.Speed) * GetSkillFactor(ship.Skills.Navigation);
|
||||
|
||||
@@ -183,6 +313,7 @@ public sealed partial class ShipAiService
|
||||
{
|
||||
var policy = ResolvePolicy(world, ship.PolicySetId);
|
||||
var preferredItemId = assignment?.ItemId ?? ship.DefaultBehavior.ItemId;
|
||||
var preferredAnchorId = ship.DefaultBehavior.PreferredAnchorId;
|
||||
var rangeBudget = ResolveBehaviorSystemRange(world, ship, behaviorKind, ship.DefaultBehavior.MaxSystemRange);
|
||||
var effectiveMiningSkill = GetEffectiveSkillLevel(world, ship, skills => skills.Mining, skills => skills.Coordination);
|
||||
string? deniedReason = null;
|
||||
@@ -194,6 +325,11 @@ public sealed partial class ShipAiService
|
||||
return false;
|
||||
}
|
||||
|
||||
if (preferredAnchorId is not null && !string.Equals(node.AnchorId, preferredAnchorId, StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryCheckSystemAllowed(world, policy, ship.FactionId, node.SystemId, "military", out var reason))
|
||||
{
|
||||
deniedReason ??= reason;
|
||||
@@ -214,7 +350,7 @@ public sealed partial class ShipAiService
|
||||
+ (effectiveMiningSkill * 10f)
|
||||
- distancePenalty
|
||||
- routeRiskPenalty
|
||||
- node.Position.DistanceTo(ship.Position);
|
||||
- ResolveNodeSystemPosition(world, node).DistanceTo(ResolveShipSystemPosition(world, ship));
|
||||
return new MiningOpportunity(node, buyer, score, $"Mine {node.ItemId} for {buyer.Label}");
|
||||
})
|
||||
.OrderByDescending(candidate => candidate.Score)
|
||||
@@ -452,7 +588,7 @@ public sealed partial class ShipAiService
|
||||
?? homeStation;
|
||||
}
|
||||
|
||||
private static ResourceNodeRuntime? SelectLocalMiningNode(SimulationWorld world, ShipRuntime ship, string systemId, string itemId)
|
||||
private static ResourceNodeRuntime? SelectLocalMiningNode(SimulationWorld world, ShipRuntime ship, string systemId, string itemId, string? anchorId = null)
|
||||
{
|
||||
var policy = ResolvePolicy(world, ship.PolicySetId);
|
||||
string? deniedReason = null;
|
||||
@@ -467,6 +603,11 @@ public sealed partial class ShipAiService
|
||||
return false;
|
||||
}
|
||||
|
||||
if (anchorId is not null && !string.Equals(candidate.AnchorId, anchorId, StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryCheckSystemAllowed(world, policy, ship.FactionId, candidate.SystemId, "military", out var reason))
|
||||
{
|
||||
deniedReason ??= reason;
|
||||
@@ -487,6 +628,54 @@ public sealed partial class ShipAiService
|
||||
return node;
|
||||
}
|
||||
|
||||
private static ResourceDepositRuntime? ResolveResourceDeposit(SimulationWorld world, string? depositId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(depositId))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (var node in world.Nodes)
|
||||
{
|
||||
var deposit = node.Deposits.FirstOrDefault(candidate => string.Equals(candidate.Id, depositId, StringComparison.Ordinal));
|
||||
if (deposit is not null)
|
||||
{
|
||||
return deposit;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ResourceDepositRuntime? SelectMiningDeposit(ResourceNodeRuntime node, string shipId)
|
||||
{
|
||||
return node.Deposits
|
||||
.Where(candidate => candidate.OreRemaining > 0.01f)
|
||||
.OrderByDescending(candidate => candidate.OreRemaining)
|
||||
.ThenBy(candidate => candidate.Id, StringComparer.Ordinal)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static void SyncNodeOreTotals(ResourceNodeRuntime node)
|
||||
{
|
||||
node.OreRemaining = node.Deposits.Sum(candidate => candidate.OreRemaining);
|
||||
}
|
||||
|
||||
private static AnchorRuntime? ResolveMiningAnchor(SimulationWorld world, string? anchorId, string? nodeId)
|
||||
{
|
||||
if (anchorId is not null)
|
||||
{
|
||||
return ResolveAnchor(world, anchorId);
|
||||
}
|
||||
|
||||
if (nodeId is not null && ResolveNode(world, nodeId) is { } node)
|
||||
{
|
||||
return ResolveAnchor(world, node.AnchorId);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static StationRuntime? SelectLocalAutoMineBuyer(SimulationWorld world, ShipRuntime ship, string systemId, string itemId)
|
||||
{
|
||||
var policy = ResolvePolicy(world, ship.PolicySetId);
|
||||
@@ -686,9 +875,14 @@ public sealed partial class ShipAiService
|
||||
return (celestial.SystemId, celestial.Position);
|
||||
}
|
||||
|
||||
if (ResolveAnchor(world, entityId) is { } anchor)
|
||||
{
|
||||
return (anchor.SystemId, anchor.Position);
|
||||
}
|
||||
|
||||
if (world.ConstructionSites.FirstOrDefault(candidate => candidate.Id == entityId) is { } site)
|
||||
{
|
||||
var position = world.Celestials.FirstOrDefault(candidate => candidate.Id == site.CelestialId)?.Position ?? Vector3.Zero;
|
||||
var position = ResolveAnchor(world, site.AnchorId)?.Position ?? Vector3.Zero;
|
||||
return (site.SystemId, position);
|
||||
}
|
||||
|
||||
@@ -720,6 +914,16 @@ public sealed partial class ShipAiService
|
||||
private static StationRuntime? ResolveStation(SimulationWorld world, string? stationId) =>
|
||||
stationId is null ? null : world.Stations.FirstOrDefault(candidate => candidate.Id == stationId);
|
||||
|
||||
private static AnchorRuntime? ResolveAnchor(SimulationWorld world, string? anchorId) =>
|
||||
anchorId is null ? null : world.Anchors.FirstOrDefault(candidate => candidate.Id == anchorId);
|
||||
|
||||
private static CelestialRuntime? ResolveAnchorBackedCelestial(SimulationWorld world, string? anchorId)
|
||||
{
|
||||
var anchor = ResolveAnchor(world, anchorId);
|
||||
var celestialId = SpatialBuilder.ResolveCompatibleCelestialId(anchor);
|
||||
return celestialId is null ? null : world.Celestials.FirstOrDefault(candidate => candidate.Id == celestialId);
|
||||
}
|
||||
|
||||
private static ResourceNodeRuntime? ResolveNode(SimulationWorld world, string? nodeId) =>
|
||||
nodeId is null ? null : world.Nodes.FirstOrDefault(candidate => candidate.Id == nodeId);
|
||||
|
||||
@@ -815,7 +1019,8 @@ public sealed partial class ShipAiService
|
||||
|
||||
if (site?.StationId is null && site is not null)
|
||||
{
|
||||
var anchorPosition = world.Celestials.FirstOrDefault(candidate => candidate.Id == site.CelestialId)?.Position ?? station.Position;
|
||||
var anchorPosition = ResolveAnchor(world, site.AnchorId)?.Position
|
||||
?? station.Position;
|
||||
return GetResourceHoldPosition(anchorPosition, ship.Id, 78f);
|
||||
}
|
||||
|
||||
@@ -867,7 +1072,7 @@ public sealed partial class ShipAiService
|
||||
|
||||
private static void CompleteStationFoundation(SimulationWorld world, StationRuntime supportStation, ConstructionSiteRuntime site)
|
||||
{
|
||||
var anchor = world.Celestials.FirstOrDefault(candidate => candidate.Id == site.CelestialId);
|
||||
var anchor = ResolveAnchor(world, site.AnchorId);
|
||||
if (anchor is null || site.BlueprintId is null)
|
||||
{
|
||||
site.State = ConstructionSiteStateKinds.Destroyed;
|
||||
@@ -878,13 +1083,13 @@ public sealed partial class ShipAiService
|
||||
{
|
||||
Id = $"station-{world.Stations.Count + 1}",
|
||||
SystemId = site.SystemId,
|
||||
AnchorId = site.AnchorId,
|
||||
Label = BuildFoundedStationLabel(site.TargetDefinitionId),
|
||||
Category = "station",
|
||||
Objective = DetermineFoundationObjective(site.TargetDefinitionId),
|
||||
Color = world.Factions.FirstOrDefault(candidate => candidate.Id == site.FactionId)?.Color ?? supportStation.Color,
|
||||
Position = anchor.Position,
|
||||
Position = Vector3.Zero,
|
||||
FactionId = site.FactionId,
|
||||
CelestialId = site.CelestialId,
|
||||
Health = 600f,
|
||||
MaxHealth = 600f,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user