176 lines
8.0 KiB
C#
176 lines
8.0 KiB
C#
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using NeonSprawl.Server.Game.AbilityInput;
|
|
using NeonSprawl.Server.Game.Npc;
|
|
using NeonSprawl.Server.Game.Targeting;
|
|
using NeonSprawl.Server.Tests;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Npc;
|
|
|
|
public sealed class NpcRuntimeSnapshotWorldApiTests
|
|
{
|
|
private static async Task BindSlot0PulseAsync(HttpClient client)
|
|
{
|
|
var post = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/hotbar-loadout",
|
|
new HotbarLoadoutUpdateRequest
|
|
{
|
|
SchemaVersion = HotbarLoadoutUpdateRequest.CurrentSchemaVersion,
|
|
Slots = [new HotbarSlotBindingJson { SlotIndex = 0, AbilityId = PrototypeAbilityRegistry.PrototypePulse }],
|
|
});
|
|
post.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
private static async Task LockPrototypeNpcMeleeAsync(HttpClient client)
|
|
{
|
|
var response = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/target/select",
|
|
new TargetSelectRequest
|
|
{
|
|
SchemaVersion = TargetSelectRequest.CurrentSchemaVersion,
|
|
TargetId = PrototypeNpcRegistry.PrototypeNpcMeleeId,
|
|
});
|
|
response.EnsureSuccessStatusCode();
|
|
}
|
|
|
|
private static AbilityCastRequest PulseCastRequest() =>
|
|
new()
|
|
{
|
|
SchemaVersion = AbilityCastRequest.CurrentSchemaVersion,
|
|
SlotIndex = 0,
|
|
AbilityId = PrototypeAbilityRegistry.PrototypePulse,
|
|
TargetId = PrototypeNpcRegistry.PrototypeNpcMeleeId,
|
|
};
|
|
|
|
[Fact]
|
|
public async Task GetNpcRuntimeSnapshot_ShouldReturnSchemaV1_WithThreeIdleInstances()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
// Act
|
|
var response = await client.GetAsync("/game/world/npc-runtime-snapshot");
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<NpcRuntimeSnapshotResponse>();
|
|
Assert.NotNull(body);
|
|
Assert.Equal(NpcRuntimeSnapshotResponse.CurrentSchemaVersion, body!.SchemaVersion);
|
|
Assert.NotEqual(default, body.ServerTimeUtc);
|
|
Assert.NotNull(body.NpcInstances);
|
|
Assert.Equal(3, body.NpcInstances.Count);
|
|
var ids = body.NpcInstances.Select(static row => row.NpcInstanceId).ToList();
|
|
Assert.Equal(PrototypeNpcRegistryTests.InstancesInIdOrder, ids);
|
|
|
|
foreach (var row in body.NpcInstances)
|
|
{
|
|
Assert.Equal("idle", row.State);
|
|
Assert.Null(row.AggroHolderPlayerId);
|
|
Assert.Null(row.ActiveTelegraph);
|
|
}
|
|
|
|
var melee = body.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcMeleeId);
|
|
Assert.Equal(PrototypeNpcBehaviorRegistry.PrototypeMeleePressure, melee.BehaviorDefId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetNpcRuntimeSnapshot_ShouldShowAggro_OnMeleeAfterDamagingCast()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
await BindSlot0PulseAsync(client);
|
|
await LockPrototypeNpcMeleeAsync(client);
|
|
var castResponse = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/ability-cast",
|
|
PulseCastRequest());
|
|
castResponse.EnsureSuccessStatusCode();
|
|
// Act
|
|
var response = await client.GetAsync("/game/world/npc-runtime-snapshot");
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<NpcRuntimeSnapshotResponse>();
|
|
Assert.NotNull(body);
|
|
var melee = body!.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcMeleeId);
|
|
Assert.Equal("aggro", melee.State);
|
|
Assert.Equal("dev-local-1", melee.AggroHolderPlayerId);
|
|
Assert.Null(melee.ActiveTelegraph);
|
|
|
|
var ranged = body.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcRangedId);
|
|
Assert.Equal("idle", ranged.State);
|
|
Assert.Null(ranged.AggroHolderPlayerId);
|
|
Assert.Null(ranged.ActiveTelegraph);
|
|
|
|
var elite = body.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcEliteId);
|
|
Assert.Equal("idle", elite.State);
|
|
Assert.Null(elite.AggroHolderPlayerId);
|
|
Assert.Null(elite.ActiveTelegraph);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetNpcRuntimeSnapshot_ShouldShowTelegraph_AfterAttackCooldownElapses()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
Assert.NotNull(factory.FakeClock);
|
|
await BindSlot0PulseAsync(client);
|
|
await LockPrototypeNpcMeleeAsync(client);
|
|
var castResponse = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/ability-cast",
|
|
PulseCastRequest());
|
|
castResponse.EnsureSuccessStatusCode();
|
|
var initResponse = await client.GetAsync("/game/world/npc-runtime-snapshot");
|
|
initResponse.EnsureSuccessStatusCode();
|
|
factory.FakeClock!.Advance(TimeSpan.FromSeconds(3));
|
|
// Act
|
|
var response = await client.GetAsync("/game/world/npc-runtime-snapshot");
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var body = await response.Content.ReadFromJsonAsync<NpcRuntimeSnapshotResponse>();
|
|
Assert.NotNull(body);
|
|
var melee = body!.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcMeleeId);
|
|
Assert.Equal("telegraph_windup", melee.State);
|
|
Assert.NotNull(melee.ActiveTelegraph);
|
|
Assert.Equal("melee_pressure", melee.ActiveTelegraph!.ArchetypeKind);
|
|
Assert.Equal(PrototypeNpcRegistry.PrototypeNpcMeleeId, melee.ActiveTelegraph.NpcInstanceId);
|
|
Assert.InRange(melee.ActiveTelegraph.WindupRemainingSeconds, 1.49, 1.51);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetNpcRuntimeSnapshot_ShouldDecreaseWindupRemaining_AcrossPolls()
|
|
{
|
|
// Arrange
|
|
await using var factory = new InMemoryWebApplicationFactory();
|
|
var client = factory.CreateClient();
|
|
Assert.NotNull(factory.FakeClock);
|
|
await BindSlot0PulseAsync(client);
|
|
await LockPrototypeNpcMeleeAsync(client);
|
|
var castResponse = await client.PostAsJsonAsync(
|
|
"/game/players/dev-local-1/ability-cast",
|
|
PulseCastRequest());
|
|
castResponse.EnsureSuccessStatusCode();
|
|
var initResponse = await client.GetAsync("/game/world/npc-runtime-snapshot");
|
|
initResponse.EnsureSuccessStatusCode();
|
|
factory.FakeClock!.Advance(TimeSpan.FromSeconds(3));
|
|
var firstResponse = await client.GetAsync("/game/world/npc-runtime-snapshot");
|
|
factory.FakeClock.Advance(TimeSpan.FromSeconds(1));
|
|
// Act
|
|
var secondResponse = await client.GetAsync("/game/world/npc-runtime-snapshot");
|
|
// Assert
|
|
Assert.Equal(HttpStatusCode.OK, firstResponse.StatusCode);
|
|
Assert.Equal(HttpStatusCode.OK, secondResponse.StatusCode);
|
|
var firstBody = await firstResponse.Content.ReadFromJsonAsync<NpcRuntimeSnapshotResponse>();
|
|
var secondBody = await secondResponse.Content.ReadFromJsonAsync<NpcRuntimeSnapshotResponse>();
|
|
Assert.NotNull(firstBody);
|
|
Assert.NotNull(secondBody);
|
|
var firstMelee = firstBody!.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcMeleeId);
|
|
var secondMelee = secondBody!.NpcInstances.Single(row => row.NpcInstanceId == PrototypeNpcRegistry.PrototypeNpcMeleeId);
|
|
Assert.NotNull(firstMelee.ActiveTelegraph);
|
|
Assert.NotNull(secondMelee.ActiveTelegraph);
|
|
Assert.True(secondMelee.ActiveTelegraph!.WindupRemainingSeconds < firstMelee.ActiveTelegraph!.WindupRemainingSeconds);
|
|
Assert.InRange(secondMelee.ActiveTelegraph.WindupRemainingSeconds, 0.49, 0.51);
|
|
}
|
|
}
|