137 lines
4.8 KiB
C#
137 lines
4.8 KiB
C#
using NeonSprawl.Server.Game.PositionState;
|
|
|
|
namespace NeonSprawl.Server.Game.Encounters;
|
|
|
|
/// <summary>Maps <c>GET /game/players/{{id}}/encounter-progress</c> (NEO-108).</summary>
|
|
public static class EncounterProgressApi
|
|
{
|
|
public const string StateInactive = "inactive";
|
|
public const string StateActive = "active";
|
|
public const string StateCompleted = "completed";
|
|
|
|
public static WebApplication MapEncounterProgressApi(this WebApplication app)
|
|
{
|
|
app.MapGet(
|
|
"/game/players/{id}/encounter-progress",
|
|
(string id, IPositionStateStore positions, IEncounterDefinitionRegistry encounterRegistry,
|
|
IEncounterProgressStore progressStore, IEncounterCompletionStore completionStore,
|
|
IEncounterCompleteEventStore completeEventStore) =>
|
|
{
|
|
var trimmedId = id.Trim();
|
|
if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
|
|
{
|
|
return Results.NotFound();
|
|
}
|
|
|
|
return Results.Json(
|
|
BuildSnapshot(
|
|
trimmedId,
|
|
encounterRegistry,
|
|
progressStore,
|
|
completionStore,
|
|
completeEventStore));
|
|
});
|
|
|
|
return app;
|
|
}
|
|
|
|
internal static EncounterProgressListResponse BuildSnapshot(
|
|
string playerId,
|
|
IEncounterDefinitionRegistry encounterRegistry,
|
|
IEncounterProgressStore progressStore,
|
|
IEncounterCompletionStore completionStore,
|
|
IEncounterCompleteEventStore completeEventStore)
|
|
{
|
|
var defs = encounterRegistry.GetDefinitionsInIdOrder();
|
|
var rows = new List<EncounterProgressRowJson>(defs.Count);
|
|
foreach (var def in defs)
|
|
{
|
|
rows.Add(
|
|
MapRow(
|
|
playerId,
|
|
def.Id,
|
|
progressStore,
|
|
completionStore,
|
|
completeEventStore));
|
|
}
|
|
|
|
return new EncounterProgressListResponse
|
|
{
|
|
PlayerId = playerId,
|
|
Encounters = rows,
|
|
SchemaVersion = EncounterProgressListResponse.CurrentSchemaVersion,
|
|
};
|
|
}
|
|
|
|
private static EncounterProgressRowJson MapRow(
|
|
string playerId,
|
|
string encounterId,
|
|
IEncounterProgressStore progressStore,
|
|
IEncounterCompletionStore completionStore,
|
|
IEncounterCompleteEventStore completeEventStore)
|
|
{
|
|
var hasProgress = progressStore.TryGetProgress(playerId, encounterId, out var progress);
|
|
var defeatedTargetIds = hasProgress
|
|
? [.. progress.DefeatedNpcInstanceIds.OrderBy(static id => id, StringComparer.Ordinal)]
|
|
: Array.Empty<string>();
|
|
|
|
if (completionStore.IsCompleted(playerId, encounterId))
|
|
{
|
|
if (!completionStore.TryGetCompletedAt(playerId, encounterId, out var completedAt))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Encounter '{encounterId}' is completed for player '{playerId}' but completedAt is missing.");
|
|
}
|
|
|
|
if (!completeEventStore.TryGet(playerId, encounterId, out var completeEvent))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Encounter '{encounterId}' is completed for player '{playerId}' but EncounterCompleteEvent is missing.");
|
|
}
|
|
|
|
return new EncounterProgressRowJson
|
|
{
|
|
EncounterId = encounterId,
|
|
State = StateCompleted,
|
|
DefeatedTargetIds = defeatedTargetIds,
|
|
CompletedAt = completedAt,
|
|
RewardGrantSummary = MapGrantSummary(completeEvent.GrantedItems),
|
|
};
|
|
}
|
|
|
|
if (hasProgress && progress.Started)
|
|
{
|
|
return new EncounterProgressRowJson
|
|
{
|
|
EncounterId = encounterId,
|
|
State = StateActive,
|
|
DefeatedTargetIds = defeatedTargetIds,
|
|
};
|
|
}
|
|
|
|
return new EncounterProgressRowJson
|
|
{
|
|
EncounterId = encounterId,
|
|
State = StateInactive,
|
|
DefeatedTargetIds = defeatedTargetIds,
|
|
};
|
|
}
|
|
|
|
/// <summary>Preserves commit-time grant order from <see cref="EncounterCompleteEvent.GrantedItems"/> (reward-table apply order).</summary>
|
|
private static List<EncounterRewardGrantJson> MapGrantSummary(IReadOnlyList<EncounterGrantApplied> grants)
|
|
{
|
|
var summary = new List<EncounterRewardGrantJson>(grants.Count);
|
|
foreach (var grant in grants)
|
|
{
|
|
summary.Add(
|
|
new EncounterRewardGrantJson
|
|
{
|
|
ItemId = grant.ItemId,
|
|
Quantity = grant.Quantity,
|
|
});
|
|
}
|
|
|
|
return summary;
|
|
}
|
|
}
|