neon-sprawl/server/NeonSprawl.Server.Tests/Game/Combat/CombatTargetsWorldApiTests.cs

153 lines
6.4 KiB
C#

using System.Linq;
using System.Net;
using System.Net.Http.Json;
using NeonSprawl.Server.Game.AbilityInput;
using NeonSprawl.Server.Game.Combat;
using NeonSprawl.Server.Game.Targeting;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.Combat;
public class CombatTargetsWorldApiTests
{
/// <summary>Prototype combat targets in registry id order (ordinal). Keep in sync with Bruno.</summary>
public static readonly string[] PrototypeTargetsInIdOrder =
[
PrototypeTargetRegistry.PrototypeTargetAlphaId,
PrototypeTargetRegistry.PrototypeTargetBetaId,
];
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 LockPrototypeTargetAlphaAsync(HttpClient client)
{
var response = await client.PostAsJsonAsync(
"/game/players/dev-local-1/target/select",
new TargetSelectRequest
{
SchemaVersion = TargetSelectRequest.CurrentSchemaVersion,
TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
});
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadFromJsonAsync<TargetSelectResponse>();
Assert.NotNull(body);
Assert.True(body!.SelectionApplied);
}
private static AbilityCastRequest PulseCastRequest() =>
new()
{
SchemaVersion = AbilityCastRequest.CurrentSchemaVersion,
SlotIndex = 0,
AbilityId = PrototypeAbilityRegistry.PrototypePulse,
TargetId = PrototypeTargetRegistry.PrototypeTargetAlphaId,
};
[Fact]
public async Task GetCombatTargets_ShouldReturnSchemaV1_WithBothTargetsAtFullHp()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("/game/world/combat-targets");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadFromJsonAsync<CombatTargetsListResponse>();
Assert.NotNull(body);
Assert.Equal(CombatTargetsListResponse.CurrentSchemaVersion, body!.SchemaVersion);
Assert.NotNull(body.Targets);
Assert.Equal(2, body.Targets.Count);
var ids = body.Targets.Select(static t => t.TargetId).ToList();
Assert.Equal(PrototypeTargetsInIdOrder, ids);
var alpha = body.Targets.Single(t => t.TargetId == PrototypeTargetRegistry.PrototypeTargetAlphaId);
Assert.Equal(PrototypeCombatConstants.MaxPrototypeTargetHp, alpha.MaxHp);
Assert.Equal(PrototypeCombatConstants.MaxPrototypeTargetHp, alpha.CurrentHp);
Assert.False(alpha.Defeated);
var beta = body.Targets.Single(t => t.TargetId == PrototypeTargetRegistry.PrototypeTargetBetaId);
Assert.Equal(PrototypeCombatConstants.MaxPrototypeTargetHp, beta.MaxHp);
Assert.Equal(PrototypeCombatConstants.MaxPrototypeTargetHp, beta.CurrentHp);
Assert.False(beta.Defeated);
}
[Fact]
public async Task GetCombatTargets_ShouldReflectCastDamage_AfterOnePulseOnAlpha()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
await BindSlot0PulseAsync(client);
await LockPrototypeTargetAlphaAsync(client);
var castResponse = await client.PostAsJsonAsync(
"/game/players/dev-local-1/ability-cast",
PulseCastRequest());
castResponse.EnsureSuccessStatusCode();
var castBody = await castResponse.Content.ReadFromJsonAsync<AbilityCastResponse>();
// Act
var response = await client.GetAsync("/game/world/combat-targets");
var body = await response.Content.ReadFromJsonAsync<CombatTargetsListResponse>();
// Assert
Assert.NotNull(castBody);
Assert.True(castBody!.Accepted);
Assert.NotNull(castBody.CombatResolution);
Assert.Equal(75, castBody.CombatResolution!.TargetRemainingHp);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.NotNull(body);
var alpha = body!.Targets.Single(t => t.TargetId == PrototypeTargetRegistry.PrototypeTargetAlphaId);
Assert.Equal(75, alpha.CurrentHp);
Assert.False(alpha.Defeated);
var beta = body.Targets.Single(t => t.TargetId == PrototypeTargetRegistry.PrototypeTargetBetaId);
Assert.Equal(PrototypeCombatConstants.MaxPrototypeTargetHp, beta.CurrentHp);
Assert.False(beta.Defeated);
}
[Fact]
public async Task GetCombatTargets_ShouldShowDefeated_AfterFourPulsesOnAlpha()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
Assert.NotNull(factory.FakeClock);
await BindSlot0PulseAsync(client);
await LockPrototypeTargetAlphaAsync(client);
var cast = PulseCastRequest();
for (var i = 0; i < 4; i++)
{
var castResponse = await client.PostAsJsonAsync("/game/players/dev-local-1/ability-cast", cast);
castResponse.EnsureSuccessStatusCode();
if (i < 3)
{
factory.FakeClock!.Advance(TimeSpan.FromSeconds(3) + TimeSpan.FromMilliseconds(50));
}
}
// Act
var response = await client.GetAsync("/game/world/combat-targets");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadFromJsonAsync<CombatTargetsListResponse>();
Assert.NotNull(body);
var alpha = body!.Targets.Single(t => t.TargetId == PrototypeTargetRegistry.PrototypeTargetAlphaId);
Assert.Equal(0, alpha.CurrentHp);
Assert.True(alpha.Defeated);
var beta = body.Targets.Single(t => t.TargetId == PrototypeTargetRegistry.PrototypeTargetBetaId);
Assert.Equal(PrototypeCombatConstants.MaxPrototypeTargetHp, beta.CurrentHp);
Assert.False(beta.Defeated);
}
}