83 lines
3.0 KiB
C#
83 lines
3.0 KiB
C#
using NeonSprawl.Server.Game.PositionState;
|
|
|
|
namespace NeonSprawl.Server.Game.AbilityInput;
|
|
|
|
/// <summary>Maps prototype ability cast POST (NEO-31).</summary>
|
|
public static class AbilityCastApi
|
|
{
|
|
public const string ReasonSlotOutOfBounds = "slot_out_of_bounds";
|
|
public const string ReasonSlotUnbound = "slot_unbound";
|
|
public const string ReasonLoadoutMismatch = "loadout_mismatch";
|
|
public const string ReasonUnknownAbility = "unknown_ability";
|
|
|
|
public static WebApplication MapAbilityCastApi(this WebApplication app)
|
|
{
|
|
app.MapPost(
|
|
"/game/players/{id}/ability-cast",
|
|
(string id, AbilityCastRequest? body, IPositionStateStore positions, IPlayerHotbarLoadoutStore store) =>
|
|
{
|
|
if (body is null || body.SchemaVersion != AbilityCastRequest.CurrentSchemaVersion)
|
|
{
|
|
return Results.BadRequest();
|
|
}
|
|
|
|
if (!positions.TryGetPosition(id, out _))
|
|
{
|
|
return Results.NotFound();
|
|
}
|
|
|
|
if (!store.TryGetBindings(id, out var bindings))
|
|
{
|
|
return Results.NotFound();
|
|
}
|
|
|
|
if (body.SlotIndex < 0 || body.SlotIndex >= HotbarLoadoutResponse.SlotCountV1)
|
|
{
|
|
return Results.Json(
|
|
new AbilityCastResponse
|
|
{
|
|
Accepted = false,
|
|
ReasonCode = ReasonSlotOutOfBounds,
|
|
});
|
|
}
|
|
|
|
if (!bindings.TryGetValue(body.SlotIndex, out var boundAbility) ||
|
|
string.IsNullOrWhiteSpace(boundAbility))
|
|
{
|
|
return Results.Json(
|
|
new AbilityCastResponse
|
|
{
|
|
Accepted = false,
|
|
ReasonCode = ReasonSlotUnbound,
|
|
});
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(body.AbilityId) ||
|
|
!PrototypeAbilityRegistry.TryNormalizeKnown(body.AbilityId, out var normalizedRequest))
|
|
{
|
|
return Results.Json(
|
|
new AbilityCastResponse
|
|
{
|
|
Accepted = false,
|
|
ReasonCode = ReasonUnknownAbility,
|
|
});
|
|
}
|
|
|
|
if (!string.Equals(normalizedRequest, boundAbility, StringComparison.Ordinal))
|
|
{
|
|
return Results.Json(
|
|
new AbilityCastResponse
|
|
{
|
|
Accepted = false,
|
|
ReasonCode = ReasonLoadoutMismatch,
|
|
});
|
|
}
|
|
|
|
// TargetId echoed for NEO-28 / E5.M1; server does not validate against lock store in NEO-31.
|
|
return Results.Json(new AbilityCastResponse { Accepted = true });
|
|
});
|
|
|
|
return app;
|
|
}
|
|
}
|