Files
space-game/tests/backend/ShipAiServiceExecutionTests.cs

154 lines
5.3 KiB
C#

using FluentAssertions;
using SpaceGame.Api.Definitions;
using SpaceGame.Api.Industry.Planning;
using SpaceGame.Api.Shared.Runtime;
using SpaceGame.Api.Ships.AI;
using SpaceGame.Api.Ships.Runtime;
using SpaceGame.Api.Universe.Contracts;
using SpaceGame.Api.Universe.Runtime;
using SpaceGame.Api.Universe.Simulation;
using Xunit;
namespace SpaceGame.Api.Tests;
public sealed class ShipAiServiceExecutionTests
{
[Fact]
public void MoveOrder_CompletesAndIsRemovedAfterArrival()
{
var ship = new ShipRuntime
{
Id = "ship-1",
SystemId = "sys-1",
Definition = CreateShipDefinition(),
FactionId = "player",
Position = Vector3.Zero,
TargetPosition = Vector3.Zero,
SpatialState = new ShipSpatialStateRuntime
{
CurrentSystemId = "sys-1",
CurrentAnchorId = null,
LocalPosition = Vector3.Zero,
SystemPosition = Vector3.Zero,
},
DefaultBehavior = new DefaultBehaviorRuntime
{
Kind = ShipBehaviorKinds.Idle,
},
Skills = new ShipSkillProfileRuntime
{
Navigation = 3,
Trade = 3,
Mining = 3,
Combat = 3,
Construction = 3,
},
Health = 100f,
};
ship.OrderQueue.EnqueuePlayerOrder(new ShipOrderRuntime
{
Id = "move-1",
Kind = ShipOrderKinds.Move,
SourceKind = ShipOrderSourceKind.Player,
SourceId = "test",
Label = "Fly to point",
TargetSystemId = "sys-1",
TargetPosition = new Vector3(5f, 0f, 0f),
});
var world = CreateWorld(ship);
var service = new ShipAiService(new TestBalanceService());
var events = new List<SimulationEventRecord>();
service.UpdateShip(world, ship, 1f, events);
service.UpdateShip(world, ship, 0.01f, events);
ship.Position.Should().Be(new Vector3(5f, 0f, 0f));
ship.OrderQueue.Should().BeEmpty();
ship.ActiveOrderId.Should().BeNull();
}
private static SimulationWorld CreateWorld(ShipRuntime ship)
{
return new SimulationWorld
{
Label = "test",
Seed = 1,
Systems =
[
new SystemRuntime
{
Definition = new SolarSystemDefinition
{
Id = "sys-1",
Label = "System 1",
Position = [0f, 0f, 0f],
Stars = [],
AsteroidField = new AsteroidFieldDefinition(),
ResourceNodes = [],
Planets = [],
},
Position = Vector3.Zero,
}
],
Anchors = [],
Nodes = [],
Celestials = [],
Wrecks = [],
Stations = [],
Ships = [ship],
Factions = [],
Commanders = [],
Claims = [],
ConstructionSites = [],
MarketOrders = [],
Policies = [],
ShipDefinitions = new Dictionary<string, ShipDefinition>(StringComparer.Ordinal),
ItemDefinitions = new Dictionary<string, ItemDefinition>(StringComparer.Ordinal),
ModuleDefinitions = new Dictionary<string, ModuleDefinition>(StringComparer.Ordinal),
ModuleRecipes = new Dictionary<string, ModuleRecipeDefinition>(StringComparer.Ordinal),
Recipes = new Dictionary<string, RecipeDefinition>(StringComparer.Ordinal),
ProductionGraph = new ProductionGraph
{
Commodities = new Dictionary<string, ProductionCommodityNode>(StringComparer.Ordinal),
Processes = new Dictionary<string, ProductionProcessNode>(StringComparer.Ordinal),
ProcessesByOutputId = new Dictionary<string, IReadOnlyList<ProductionProcessNode>>(StringComparer.Ordinal),
ProcessesByInputId = new Dictionary<string, IReadOnlyList<ProductionProcessNode>>(StringComparer.Ordinal),
OutputsByModuleId = new Dictionary<string, IReadOnlyList<string>>(StringComparer.Ordinal),
},
GeneratedAtUtc = DateTimeOffset.UtcNow,
};
}
private static ShipDefinition CreateShipDefinition()
{
return new ShipDefinition
{
Id = "ship-def",
Name = "Test Ship",
Size = "small",
Hull = 100f,
Purpose = ShipPurpose.Trade,
Type = ShipType.Courier,
};
}
private sealed class TestBalanceService : IBalanceService
{
public float SimulationSpeedMultiplier => 1f;
public float YPlane => 0f;
public float ArrivalThreshold => 1f;
public float MiningRate => 10f;
public float MiningCycleSeconds => 1f;
public float TransferRate => 10f;
public float DockingDuration => 0.1f;
public float UndockingDuration => 0.1f;
public float UndockDistance => 10f;
public BalanceOptions GetCurrent() => new();
public BalanceOptions Update(BalanceOptions candidate) => candidate;
}
}