Refine ship orders and viewer controls
This commit is contained in:
@@ -129,6 +129,39 @@ public sealed class WorldService
|
||||
}
|
||||
}
|
||||
|
||||
public ShipSnapshot? UpdateShipOrder(string shipId, string orderId, ShipOrderUpdateCommandRequest request)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
ValidateShipOrderRequestUnsafe(shipId, ToCommandRequest(request));
|
||||
var ship = CanCurrentActorAccessGm()
|
||||
? UpdateGmShipOrderUnsafe(shipId, orderId, request)
|
||||
: _playerFaction.UpdateDirectShipOrder(_world, _playerStateStore, GetCurrentPlayerKey(), shipId, orderId, request);
|
||||
if (ship is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetShipSnapshotUnsafe(ship.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public ShipSnapshot? ReorderShipOrder(string shipId, string orderId, ShipOrderReorderRequest request)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
var ship = CanCurrentActorAccessGm()
|
||||
? ReorderGmShipOrderUnsafe(shipId, orderId, request.TargetIndex)
|
||||
: _playerFaction.ReorderDirectShipOrder(_world, _playerStateStore, GetCurrentPlayerKey(), shipId, orderId, request.TargetIndex);
|
||||
if (ship is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetShipSnapshotUnsafe(ship.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public ShipSnapshot? UpdateShipDefaultBehavior(string shipId, ShipDefaultBehaviorCommandRequest request)
|
||||
{
|
||||
lock (_sync)
|
||||
@@ -694,6 +727,30 @@ public sealed class WorldService
|
||||
}
|
||||
}
|
||||
|
||||
private static void ApplyShipOrderRequest(ShipOrderRuntime order, ShipOrderUpdateCommandRequest request)
|
||||
{
|
||||
order.Priority = request.Priority;
|
||||
order.InterruptCurrentPlan = request.InterruptCurrentPlan;
|
||||
order.Label = request.Label;
|
||||
order.TargetEntityId = request.TargetEntityId;
|
||||
order.TargetSystemId = request.TargetSystemId;
|
||||
order.TargetPosition = request.TargetPosition is null
|
||||
? null
|
||||
: new Vector3(request.TargetPosition.X, request.TargetPosition.Y, request.TargetPosition.Z);
|
||||
order.SourceStationId = request.SourceStationId;
|
||||
order.DestinationStationId = request.DestinationStationId;
|
||||
order.ItemId = request.ItemId;
|
||||
order.AnchorId = request.AnchorId;
|
||||
order.ConstructionSiteId = request.ConstructionSiteId;
|
||||
order.ModuleId = request.ModuleId;
|
||||
order.WaitSeconds = MathF.Max(0f, request.WaitSeconds ?? 0f);
|
||||
order.Radius = MathF.Max(0f, request.Radius ?? 0f);
|
||||
order.MaxSystemRange = request.MaxSystemRange;
|
||||
order.KnownStationsOnly = request.KnownStationsOnly ?? false;
|
||||
order.Status = OrderStatus.Queued;
|
||||
order.FailureReason = null;
|
||||
}
|
||||
|
||||
private ShipRuntime? EnqueueGmShipOrderUnsafe(string shipId, ShipOrderCommandRequest request)
|
||||
{
|
||||
var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId);
|
||||
@@ -702,12 +759,7 @@ public sealed class WorldService
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ship.OrderQueue.Count >= 8)
|
||||
{
|
||||
throw new InvalidOperationException("Order queue is full.");
|
||||
}
|
||||
|
||||
ship.OrderQueue.Add(new ShipOrderRuntime
|
||||
ship.OrderQueue.EnqueuePlayerOrder(new ShipOrderRuntime
|
||||
{
|
||||
Id = $"order-{ship.Id}-{Guid.NewGuid():N}",
|
||||
Kind = request.Kind,
|
||||
@@ -732,12 +784,7 @@ public sealed class WorldService
|
||||
});
|
||||
|
||||
ship.ControlSourceKind = "gm-order";
|
||||
ship.ControlSourceId = ship.OrderQueue
|
||||
.Where(order => order.SourceKind == ShipOrderSourceKind.Player)
|
||||
.OrderByDescending(order => order.Priority)
|
||||
.ThenBy(order => order.CreatedAtUtc)
|
||||
.Select(order => order.Id)
|
||||
.FirstOrDefault();
|
||||
ship.ControlSourceId = ship.OrderQueue.FindLeadingOrderForSource(ShipOrderSourceKind.Player)?.Id;
|
||||
ship.ControlReason = request.Label ?? request.Kind;
|
||||
ship.NeedsReplan = true;
|
||||
ship.LastReplanReason = "gm-order-enqueued";
|
||||
@@ -753,22 +800,12 @@ public sealed class WorldService
|
||||
return null;
|
||||
}
|
||||
|
||||
ship.OrderQueue.RemoveAll(order => order.Id == orderId);
|
||||
ship.ControlSourceKind = ship.OrderQueue.Any(order => order.SourceKind == ShipOrderSourceKind.Player)
|
||||
ship.OrderQueue.RemoveById(orderId);
|
||||
ship.ControlSourceKind = ship.OrderQueue.HasOrdersFromSource(ShipOrderSourceKind.Player)
|
||||
? "gm-order"
|
||||
: "gm-manual";
|
||||
ship.ControlSourceId = ship.OrderQueue
|
||||
.Where(order => order.SourceKind == ShipOrderSourceKind.Player)
|
||||
.OrderByDescending(order => order.Priority)
|
||||
.ThenBy(order => order.CreatedAtUtc)
|
||||
.Select(order => order.Id)
|
||||
.FirstOrDefault();
|
||||
ship.ControlReason = ship.OrderQueue
|
||||
.Where(order => order.SourceKind == ShipOrderSourceKind.Player)
|
||||
.OrderByDescending(order => order.Priority)
|
||||
.ThenBy(order => order.CreatedAtUtc)
|
||||
.Select(order => order.Label ?? order.Kind)
|
||||
.FirstOrDefault()
|
||||
ship.ControlSourceId = ship.OrderQueue.FindLeadingOrderForSource(ShipOrderSourceKind.Player)?.Id;
|
||||
ship.ControlReason = ship.OrderQueue.GetLeadingOrderLabelForSource(ShipOrderSourceKind.Player)
|
||||
?? "manual-gm-control";
|
||||
ship.NeedsReplan = true;
|
||||
ship.LastReplanReason = "gm-order-removed";
|
||||
@@ -776,6 +813,59 @@ public sealed class WorldService
|
||||
return ship;
|
||||
}
|
||||
|
||||
private ShipRuntime? UpdateGmShipOrderUnsafe(string shipId, string orderId, ShipOrderUpdateCommandRequest request)
|
||||
{
|
||||
var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId);
|
||||
if (ship is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var order = ship.OrderQueue.FindById(orderId);
|
||||
if (order is null || order.SourceKind != ShipOrderSourceKind.Player)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ApplyShipOrderRequest(order, request);
|
||||
ship.ControlSourceKind = ship.OrderQueue.HasOrdersFromSource(ShipOrderSourceKind.Player)
|
||||
? "gm-order"
|
||||
: "gm-manual";
|
||||
ship.ControlSourceId = ship.OrderQueue.FindLeadingOrderForSource(ShipOrderSourceKind.Player)?.Id;
|
||||
ship.ControlReason = ship.OrderQueue.GetLeadingOrderLabelForSource(ShipOrderSourceKind.Player)
|
||||
?? request.Label
|
||||
?? request.Kind;
|
||||
ship.NeedsReplan = true;
|
||||
ship.LastReplanReason = "gm-order-updated";
|
||||
ship.LastDeltaSignature = string.Empty;
|
||||
return ship;
|
||||
}
|
||||
|
||||
private ShipRuntime? ReorderGmShipOrderUnsafe(string shipId, string orderId, int targetIndex)
|
||||
{
|
||||
var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId);
|
||||
if (ship is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ship.OrderQueue.TryMovePlayerOrder(orderId, targetIndex))
|
||||
{
|
||||
return ship;
|
||||
}
|
||||
|
||||
ship.ControlSourceKind = ship.OrderQueue.HasOrdersFromSource(ShipOrderSourceKind.Player)
|
||||
? "gm-order"
|
||||
: "gm-manual";
|
||||
ship.ControlSourceId = ship.OrderQueue.FindLeadingOrderForSource(ShipOrderSourceKind.Player)?.Id;
|
||||
ship.ControlReason = ship.OrderQueue.GetLeadingOrderLabelForSource(ShipOrderSourceKind.Player)
|
||||
?? "manual-gm-control";
|
||||
ship.NeedsReplan = true;
|
||||
ship.LastReplanReason = "gm-order-reordered";
|
||||
ship.LastDeltaSignature = string.Empty;
|
||||
return ship;
|
||||
}
|
||||
|
||||
private ShipRuntime? ConfigureGmShipBehaviorUnsafe(string shipId, ShipDefaultBehaviorCommandRequest request)
|
||||
{
|
||||
var ship = _world.Ships.FirstOrDefault(candidate => candidate.Id == shipId);
|
||||
@@ -837,6 +927,26 @@ public sealed class WorldService
|
||||
return ship;
|
||||
}
|
||||
|
||||
private static ShipOrderCommandRequest ToCommandRequest(ShipOrderUpdateCommandRequest request) =>
|
||||
new(
|
||||
request.Kind,
|
||||
request.Priority,
|
||||
request.InterruptCurrentPlan,
|
||||
request.Label,
|
||||
request.TargetEntityId,
|
||||
request.TargetSystemId,
|
||||
request.TargetPosition,
|
||||
request.SourceStationId,
|
||||
request.DestinationStationId,
|
||||
request.ItemId,
|
||||
request.AnchorId,
|
||||
request.ConstructionSiteId,
|
||||
request.ModuleId,
|
||||
request.WaitSeconds,
|
||||
request.Radius,
|
||||
request.MaxSystemRange,
|
||||
request.KnownStationsOnly);
|
||||
|
||||
private CommanderRuntime CreateFactionCommander(FactionRuntime faction) => new()
|
||||
{
|
||||
Id = $"commander-faction-{faction.Id}",
|
||||
@@ -915,12 +1025,15 @@ public sealed class WorldService
|
||||
return new Vector3(MathF.Cos(angle) * radius, 0f, MathF.Sin(angle) * radius);
|
||||
}
|
||||
|
||||
private AnchorRuntime? ResolveNearestConstructibleAnchor(string systemId, Vector3 position) =>
|
||||
_world.Anchors
|
||||
.Where(candidate => string.Equals(candidate.SystemId, systemId, StringComparison.Ordinal))
|
||||
.Where(candidate => SpatialBuilder.IsConstructibleAnchorKind(candidate.Kind))
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(position))
|
||||
.FirstOrDefault();
|
||||
private AnchorRuntime? ResolveNearestConstructibleAnchor(string systemId, Vector3 position)
|
||||
{
|
||||
var systemPosition = SimulationUnits.MetersToKilometers(position);
|
||||
return _world.Anchors
|
||||
.Where(candidate => string.Equals(candidate.SystemId, systemId, StringComparison.Ordinal))
|
||||
.Where(candidate => SpatialBuilder.IsConstructibleAnchorKind(candidate.Kind))
|
||||
.OrderBy(candidate => candidate.Position.DistanceTo(systemPosition))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private string? ResolveNearestAnchorId(string systemId, Vector3 position) =>
|
||||
ResolveNearestConstructibleAnchor(systemId, position)?.Id;
|
||||
|
||||
Reference in New Issue
Block a user