462 lines
22 KiB
C#
462 lines
22 KiB
C#
using static SpaceGame.Api.Shared.Runtime.SimulationRuntimeSupport;
|
|
using static SpaceGame.Api.Shared.Runtime.ShipBehaviorKinds;
|
|
using static SpaceGame.Api.Stations.Simulation.InfrastructureSimulationService;
|
|
using static SpaceGame.Api.Stations.Simulation.StationSimulationService;
|
|
|
|
namespace SpaceGame.Api.Ships.AI;
|
|
|
|
public sealed partial class ShipAiService
|
|
{
|
|
private ShipPlanRuntime? BuildEmergencyPlan(SimulationWorld world, ShipRuntime ship)
|
|
{
|
|
var policy = ResolvePolicy(world, ship.PolicySetId);
|
|
if (policy is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var hullRatio = ship.Definition.Hull <= 0.01f ? 1f : ship.Health / ship.Definition.Hull;
|
|
if (hullRatio > policy.FleeHullRatio)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var hostileNearby = world.Ships.Any(candidate =>
|
|
candidate.Health > 0f &&
|
|
candidate.FactionId != ship.FactionId &&
|
|
candidate.SystemId == ship.SystemId &&
|
|
candidate.Position.DistanceTo(ship.Position) <= 200f);
|
|
if (!hostileNearby)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var safeStation = world.Stations
|
|
.Where(station => station.FactionId == ship.FactionId)
|
|
.OrderByDescending(station => station.SystemId == ship.SystemId ? 1 : 0)
|
|
.ThenBy(station => station.Position.DistanceTo(ship.Position))
|
|
.FirstOrDefault();
|
|
|
|
var plan = new ShipPlanRuntime
|
|
{
|
|
Id = $"plan-{ship.Id}-safety-{Guid.NewGuid():N}",
|
|
SourceKind = AiPlanSourceKind.Rule,
|
|
SourceId = ShipOrderKinds.Flee,
|
|
Kind = "safety-flee",
|
|
Summary = "Emergency retreat",
|
|
};
|
|
|
|
if (safeStation is null)
|
|
{
|
|
plan.Steps.Add(CreateStep("step-flee-hold", ShipOrderKinds.HoldPosition, "Hold position away from hostiles",
|
|
[
|
|
CreateSubTask("sub-flee-hold", ShipTaskKinds.HoldPosition, "Hold safe position", ship.SystemId, ship.Position, null, 0f, 3f)
|
|
]));
|
|
return plan;
|
|
}
|
|
|
|
plan.Steps.Add(CreateStep("step-flee-travel", "travel", "Travel to safe station",
|
|
[
|
|
CreateSubTask("sub-flee-travel", ShipTaskKinds.Travel, $"Travel to {safeStation.Label}", safeStation.SystemId, safeStation.Position, safeStation.Id, MathF.Max(balance.ArrivalThreshold, safeStation.Radius + 12f), 0f)
|
|
]));
|
|
plan.Steps.Add(CreateStep("step-flee-dock", "dock", "Dock at safe station",
|
|
[
|
|
CreateSubTask("sub-flee-dock", ShipTaskKinds.Dock, $"Dock at {safeStation.Label}", safeStation.SystemId, safeStation.Position, safeStation.Id, 4f, 0f)
|
|
]));
|
|
return plan;
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
return order.Kind switch
|
|
{
|
|
var kind when string.Equals(kind, ShipOrderKinds.Move, StringComparison.Ordinal) => BuildMovePlan(ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.DockAtStation, StringComparison.Ordinal) => BuildDockOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.DockAndWait, StringComparison.Ordinal) => BuildDockAndWaitOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.FlyAndWait, StringComparison.Ordinal) => BuildFlyAndWaitOrderPlan(ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.FlyToObject, StringComparison.Ordinal) => BuildFlyToObjectOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.FollowShip, StringComparison.Ordinal) => BuildFollowShipOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.TradeRoute, StringComparison.Ordinal) => BuildTradeOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.MineAndDeliver, StringComparison.Ordinal) => BuildMineOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.MineLocal, StringComparison.Ordinal) => BuildMineLocalOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.MineAndDeliverRun, StringComparison.Ordinal) => BuildMineAndDeliverRunOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.SellMinedCargo, StringComparison.Ordinal) => BuildSellMinedCargoOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.SupplyFleetRun, StringComparison.Ordinal) => BuildSupplyFleetOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.SalvageRun, StringComparison.Ordinal) => BuildAutoSalvageOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.BuildAtSite, StringComparison.Ordinal) => BuildBuildOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.AttackTarget, StringComparison.Ordinal) => BuildAttackOrderPlan(world, ship, order),
|
|
var kind when string.Equals(kind, ShipOrderKinds.HoldPosition, StringComparison.Ordinal) => BuildHoldOrderPlan(ship, order),
|
|
_ => null,
|
|
};
|
|
}
|
|
|
|
private static (string BehaviorKind, string SourceId) ResolveBehaviorSource(SimulationWorld world, ShipRuntime ship)
|
|
{
|
|
var assignment = ResolveAssignment(world, ship);
|
|
return assignment is null
|
|
? (ship.DefaultBehavior.Kind, ship.DefaultBehavior.Kind)
|
|
: (assignment.BehaviorKind, assignment.ObjectiveId);
|
|
}
|
|
|
|
private ShipPlanRuntime BuildMovePlan(ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var targetSystemId = order.TargetSystemId ?? ship.SystemId;
|
|
var targetPosition = order.TargetPosition ?? ship.Position;
|
|
return CreatePlan(
|
|
ship,
|
|
AiPlanSourceKind.Order,
|
|
order.Id,
|
|
ShipOrderKinds.Move,
|
|
order.Label ?? "Move order",
|
|
[
|
|
CreateStep("step-move", "travel", order.Label ?? "Travel",
|
|
[
|
|
CreateSubTask("sub-move-travel", ShipTaskKinds.Travel, order.Label ?? "Travel", targetSystemId, targetPosition, order.TargetEntityId, 0f, 0f)
|
|
])
|
|
]);
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildDockOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var station = ResolveStation(world, order.TargetEntityId ?? order.DestinationStationId);
|
|
if (station is null)
|
|
{
|
|
order.FailureReason = "station-missing";
|
|
return null;
|
|
}
|
|
|
|
return CreatePlan(
|
|
ship,
|
|
AiPlanSourceKind.Order,
|
|
order.Id,
|
|
"dock-at-station",
|
|
order.Label ?? $"Dock at {station.Label}",
|
|
[
|
|
CreateStep("step-dock-travel", "travel", $"Travel to {station.Label}",
|
|
[
|
|
CreateSubTask("sub-dock-travel", ShipTaskKinds.Travel, $"Travel to {station.Label}", station.SystemId, station.Position, station.Id, MathF.Max(balance.ArrivalThreshold, station.Radius + 12f), 0f)
|
|
]),
|
|
CreateStep("step-dock", "dock", $"Dock at {station.Label}",
|
|
[
|
|
CreateSubTask("sub-dock", ShipTaskKinds.Dock, $"Dock at {station.Label}", station.SystemId, station.Position, station.Id, 4f, 0f)
|
|
])
|
|
]);
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildTradeOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
if (order.SourceStationId is null || order.DestinationStationId is null || order.ItemId is null)
|
|
{
|
|
order.FailureReason = "trade-order-incomplete";
|
|
return null;
|
|
}
|
|
|
|
var route = ResolveTradeRoute(world, order.ItemId, order.SourceStationId, order.DestinationStationId);
|
|
if (route is null)
|
|
{
|
|
order.FailureReason = "trade-route-missing";
|
|
return null;
|
|
}
|
|
|
|
return BuildTradePlan(ship, AiPlanSourceKind.Order, order.Id, route, order.Label ?? route.Summary);
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildMineOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var systemId = order.TargetSystemId ?? ship.SystemId;
|
|
var itemId = order.ItemId;
|
|
if (string.IsNullOrWhiteSpace(itemId))
|
|
{
|
|
order.FailureReason = "mine-order-item-missing";
|
|
return null;
|
|
}
|
|
|
|
var node = ResolveNode(world, order.NodeId);
|
|
if (node is not null)
|
|
{
|
|
if (!string.Equals(node.SystemId, systemId, StringComparison.Ordinal))
|
|
{
|
|
order.FailureReason = "mine-order-node-system-mismatch";
|
|
return null;
|
|
}
|
|
|
|
if (!string.Equals(node.ItemId, itemId, StringComparison.Ordinal))
|
|
{
|
|
order.FailureReason = "mine-order-node-item-mismatch";
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
node = SelectLocalMiningNode(world, ship, systemId, itemId);
|
|
}
|
|
|
|
if (node is null)
|
|
{
|
|
order.FailureReason = "mine-order-node-missing";
|
|
return null;
|
|
}
|
|
|
|
return BuildLocalMiningPlan(ship, AiPlanSourceKind.Order, order.Id, node, order.Label ?? $"Mine {itemId} in {systemId}");
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildMineLocalOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var node = ResolveNode(world, order.NodeId);
|
|
if (node is null)
|
|
{
|
|
order.FailureReason = "mine-order-incomplete";
|
|
return null;
|
|
}
|
|
|
|
return BuildLocalMiningPlan(ship, AiPlanSourceKind.Order, order.Id, node, order.Label ?? $"Mine {node.ItemId}");
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildMineAndDeliverRunOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var node = ResolveNode(world, order.NodeId);
|
|
var buyer = ResolveStation(world, order.DestinationStationId);
|
|
if (node is null || buyer is null)
|
|
{
|
|
order.FailureReason = "mine-and-deliver-order-incomplete";
|
|
return null;
|
|
}
|
|
|
|
return BuildMiningPlan(ship, AiPlanSourceKind.Order, order.Id, node, buyer, order.Label ?? $"Mine {node.ItemId} for {buyer.Label}");
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildSellMinedCargoOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var buyer = ResolveStation(world, order.DestinationStationId ?? order.TargetEntityId);
|
|
if (buyer is null || string.IsNullOrWhiteSpace(order.ItemId))
|
|
{
|
|
order.FailureReason = "sell-order-incomplete";
|
|
return null;
|
|
}
|
|
|
|
return BuildLocalMiningDeliveryPlan(ship, AiPlanSourceKind.Order, order.Id, buyer, order.ItemId, order.Label ?? $"Sell {order.ItemId}");
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildAutoSalvageOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var homeStation = ResolveStation(world, order.SourceStationId ?? ship.DefaultBehavior.HomeStationId);
|
|
var wreck = world.Wrecks.FirstOrDefault(candidate => candidate.Id == order.TargetEntityId && candidate.RemainingAmount > 0.01f);
|
|
if (homeStation is null || wreck is null)
|
|
{
|
|
order.FailureReason = "salvage-order-incomplete";
|
|
return null;
|
|
}
|
|
|
|
var approach = GetFormationPosition(wreck.Position, ship.Id, MathF.Max(8f, order.Radius > 0f ? order.Radius : ship.DefaultBehavior.Radius * 0.25f));
|
|
return CreatePlan(
|
|
ship,
|
|
AiPlanSourceKind.Order,
|
|
order.Id,
|
|
AutoSalvage,
|
|
order.Label ?? $"Salvage {wreck.ItemId}",
|
|
[
|
|
CreateStep("step-salvage-collect", "salvage", $"Salvage {wreck.ItemId}",
|
|
[
|
|
CreateSubTask("sub-salvage-travel", ShipTaskKinds.Travel, $"Travel to wreck {wreck.Id}", wreck.SystemId, approach, wreck.Id, 8f, 0f),
|
|
CreateSubTask("sub-salvage-work", ShipTaskKinds.SalvageWreck, $"Salvage {wreck.ItemId}", wreck.SystemId, approach, wreck.Id, 8f, ship.Definition.GetTotalCargoCapacity(), itemId: wreck.ItemId),
|
|
]),
|
|
CreateStep("step-salvage-deliver", "deliver-salvage", $"Deliver salvage to {homeStation.Label}",
|
|
[
|
|
CreateSubTask("sub-salvage-deliver-travel", ShipTaskKinds.Travel, $"Travel to {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, MathF.Max(homeStation.Radius + 12f, 12f), 0f),
|
|
CreateSubTask("sub-salvage-deliver-dock", ShipTaskKinds.Dock, $"Dock at {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, 4f, 0f),
|
|
CreateSubTask("sub-salvage-deliver-unload", ShipTaskKinds.UnloadCargo, $"Unload salvage at {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, 0f, ship.Definition.GetTotalCargoCapacity(), itemId: wreck.ItemId),
|
|
CreateSubTask("sub-salvage-deliver-undock", ShipTaskKinds.Undock, $"Undock from {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, 12f, 0f),
|
|
])
|
|
]);
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildSupplyFleetOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var sourceStation = ResolveStation(world, order.SourceStationId);
|
|
var targetShip = world.Ships.FirstOrDefault(candidate => candidate.Id == order.TargetEntityId && candidate.Health > 0f);
|
|
if (sourceStation is null || targetShip is null || string.IsNullOrWhiteSpace(order.ItemId))
|
|
{
|
|
order.FailureReason = "supply-fleet-order-incomplete";
|
|
return null;
|
|
}
|
|
|
|
var amount = MathF.Min(
|
|
MathF.Max(10f, ship.Definition.GetTotalCargoCapacity() * 0.5f),
|
|
GetInventoryAmount(sourceStation.Inventory, order.ItemId));
|
|
if (amount <= 0.01f)
|
|
{
|
|
order.FailureReason = "supply-item-unavailable";
|
|
return null;
|
|
}
|
|
|
|
var plan = new FleetSupplyPlan(
|
|
sourceStation,
|
|
targetShip,
|
|
order.ItemId,
|
|
amount,
|
|
MathF.Max(16f, order.Radius),
|
|
order.Label ?? $"Supply {targetShip.Definition.Name} with {order.ItemId}");
|
|
return BuildFleetSupplyPlan(ship, AiPlanSourceKind.Order, order.Id, plan);
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildBuildOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var site = world.ConstructionSites.FirstOrDefault(candidate => candidate.Id == (order.ConstructionSiteId ?? order.TargetEntityId));
|
|
if (site is null)
|
|
{
|
|
order.FailureReason = "construction-site-missing";
|
|
return null;
|
|
}
|
|
|
|
var supportStation = ResolveSupportStation(world, ship, site);
|
|
if (supportStation is null)
|
|
{
|
|
order.FailureReason = "support-station-missing";
|
|
return null;
|
|
}
|
|
|
|
return BuildConstructionPlan(ship, AiPlanSourceKind.Order, order.Id, site, supportStation, order.Label ?? $"Build {site.BlueprintId}");
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildAttackOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var targetId = order.TargetEntityId;
|
|
if (targetId is null)
|
|
{
|
|
order.FailureReason = "attack-target-missing";
|
|
return null;
|
|
}
|
|
|
|
return BuildAttackPlan(ship, AiPlanSourceKind.Order, order.Id, targetId, order.TargetSystemId, order.Label ?? "Attack target");
|
|
}
|
|
|
|
private ShipPlanRuntime BuildHoldOrderPlan(ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
return CreatePlan(
|
|
ship,
|
|
AiPlanSourceKind.Order,
|
|
order.Id,
|
|
ShipOrderKinds.HoldPosition,
|
|
order.Label ?? "Hold position",
|
|
[
|
|
CreateStep("step-hold", ShipOrderKinds.HoldPosition, order.Label ?? "Hold position",
|
|
[
|
|
CreateSubTask("sub-hold", ShipTaskKinds.HoldPosition, order.Label ?? "Hold position", order.TargetSystemId ?? ship.SystemId, order.TargetPosition ?? ship.Position, order.TargetEntityId, 0f, 3f)
|
|
])
|
|
]);
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildDockAndWaitOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var station = ResolveStation(world, order.TargetEntityId ?? order.DestinationStationId ?? ship.DefaultBehavior.HomeStationId);
|
|
if (station is null)
|
|
{
|
|
order.FailureReason = "station-missing";
|
|
return null;
|
|
}
|
|
|
|
return BuildDockAndWaitPlan(ship, AiPlanSourceKind.Order, order.Id, station, MathF.Max(1f, order.WaitSeconds), order.Label ?? $"Dock and wait at {station.Label}");
|
|
}
|
|
|
|
private ShipPlanRuntime BuildFlyAndWaitOrderPlan(ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var systemId = order.TargetSystemId ?? ship.SystemId;
|
|
var targetPosition = order.TargetPosition ?? ship.Position;
|
|
return BuildFlyAndWaitPlan(ship, AiPlanSourceKind.Order, order.Id, systemId, targetPosition, MathF.Max(1f, order.WaitSeconds), order.Label ?? "Fly and wait");
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildFlyToObjectOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var targetEntityId = order.TargetEntityId;
|
|
if (targetEntityId is null)
|
|
{
|
|
order.FailureReason = "target-missing";
|
|
return null;
|
|
}
|
|
|
|
var objectTarget = ResolveObjectTarget(world, targetEntityId);
|
|
if (objectTarget is null)
|
|
{
|
|
order.FailureReason = "target-missing";
|
|
return null;
|
|
}
|
|
|
|
return BuildFlyToObjectPlan(ship, AiPlanSourceKind.Order, order.Id, objectTarget.Value.SystemId, objectTarget.Value.Position, targetEntityId, order.Label ?? $"Fly to {targetEntityId}");
|
|
}
|
|
|
|
private ShipPlanRuntime? BuildFollowShipOrderPlan(SimulationWorld world, ShipRuntime ship, ShipOrderRuntime order)
|
|
{
|
|
var targetShip = world.Ships.FirstOrDefault(candidate => candidate.Id == order.TargetEntityId && candidate.Health > 0f);
|
|
if (targetShip is null)
|
|
{
|
|
order.FailureReason = "target-ship-missing";
|
|
return null;
|
|
}
|
|
|
|
return BuildFollowShipPlan(ship, AiPlanSourceKind.Order, order.Id, targetShip, MathF.Max(order.Radius, 18f), MathF.Max(2f, order.WaitSeconds), order.Label ?? $"Follow {targetShip.Definition.Name}");
|
|
}
|
|
|
|
private ShipPlanRuntime BuildMiningPlan(ShipRuntime ship, AiPlanSourceKind sourceKind, string sourceId, ResourceNodeRuntime node, StationRuntime homeStation, string summary)
|
|
{
|
|
var extractionPosition = GetResourceHoldPosition(node.Position, ship.Id, 20f);
|
|
return CreatePlan(
|
|
ship,
|
|
sourceKind,
|
|
sourceId,
|
|
ShipOrderKinds.MineAndDeliver,
|
|
summary,
|
|
[
|
|
CreateStep("step-mine", "mine", $"Mine {node.ItemId}",
|
|
[
|
|
CreateSubTask("sub-mine-travel", ShipTaskKinds.Travel, $"Travel to {node.ItemId} node", node.SystemId, extractionPosition, node.Id, 8f, 0f),
|
|
CreateSubTask("sub-mine", ShipTaskKinds.MineNode, $"Mine {node.ItemId}", node.SystemId, extractionPosition, node.Id, 8f, ship.Definition.GetTotalCargoCapacity())
|
|
]),
|
|
CreateStep("step-deliver", "deliver", $"Deliver {node.ItemId} to {homeStation.Label}",
|
|
[
|
|
CreateSubTask("sub-deliver-travel", ShipTaskKinds.Travel, $"Travel to {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, MathF.Max(homeStation.Radius + 12f, 12f), 0f),
|
|
CreateSubTask("sub-deliver-dock", ShipTaskKinds.Dock, $"Dock at {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, 4f, 0f),
|
|
CreateSubTask("sub-deliver-unload", ShipTaskKinds.UnloadCargo, $"Unload at {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, 0f, ship.Definition.GetTotalCargoCapacity()),
|
|
CreateSubTask("sub-deliver-undock", ShipTaskKinds.Undock, $"Undock from {homeStation.Label}", homeStation.SystemId, homeStation.Position, homeStation.Id, MathF.Max(4f, 12f), 0f)
|
|
])
|
|
]);
|
|
}
|
|
|
|
private ShipPlanRuntime BuildLocalMiningPlan(ShipRuntime ship, AiPlanSourceKind sourceKind, string sourceId, ResourceNodeRuntime node, string summary)
|
|
{
|
|
var extractionPosition = GetResourceHoldPosition(node.Position, ship.Id, 20f);
|
|
return CreatePlan(
|
|
ship,
|
|
sourceKind,
|
|
sourceId,
|
|
ShipOrderKinds.MineLocal,
|
|
summary,
|
|
[
|
|
CreateStep("step-mine", "mine", $"Mine {node.ItemId}",
|
|
[
|
|
CreateSubTask("sub-mine-travel", ShipTaskKinds.Travel, $"Travel to {node.ItemId} node", node.SystemId, extractionPosition, node.Id, 8f, 0f),
|
|
CreateSubTask("sub-mine", ShipTaskKinds.MineNode, $"Mine {node.ItemId}", node.SystemId, extractionPosition, node.Id, 8f, ship.Definition.GetTotalCargoCapacity(), itemId: node.ItemId)
|
|
])
|
|
]);
|
|
}
|
|
|
|
private ShipPlanRuntime BuildLocalMiningDeliveryPlan(ShipRuntime ship, AiPlanSourceKind sourceKind, string sourceId, StationRuntime buyer, string itemId, string summary)
|
|
{
|
|
var amount = MathF.Max(1f, GetInventoryAmount(ship.Inventory, itemId));
|
|
return CreatePlan(
|
|
ship,
|
|
sourceKind,
|
|
sourceId,
|
|
ShipOrderKinds.SellMinedCargo,
|
|
summary,
|
|
[
|
|
CreateStep("step-deliver", "deliver", $"Deliver {itemId} to {buyer.Label}",
|
|
[
|
|
CreateSubTask("sub-deliver-travel", ShipTaskKinds.Travel, $"Travel to {buyer.Label}", buyer.SystemId, buyer.Position, buyer.Id, MathF.Max(buyer.Radius + 12f, 12f), 0f),
|
|
CreateSubTask("sub-deliver-dock", ShipTaskKinds.Dock, $"Dock at {buyer.Label}", buyer.SystemId, buyer.Position, buyer.Id, 4f, 0f),
|
|
CreateSubTask("sub-deliver-unload", ShipTaskKinds.UnloadCargo, $"Unload {itemId} at {buyer.Label}", buyer.SystemId, buyer.Position, buyer.Id, 0f, amount, itemId: itemId),
|
|
CreateSubTask("sub-deliver-undock", ShipTaskKinds.Undock, $"Undock from {buyer.Label}", buyer.SystemId, buyer.Position, buyer.Id, MathF.Max(4f, 12f), 0f)
|
|
])
|
|
]);
|
|
}
|
|
}
|