neon-sprawl/server/NeonSprawl.Server/Game/Quests/QuestProgressApi.cs

177 lines
6.3 KiB
C#

using NeonSprawl.Server.Game.Items;
using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Rewards;
using NeonSprawl.Server.Game.Skills;
namespace NeonSprawl.Server.Game.Quests;
/// <summary>Maps <c>GET /game/players/{{id}}/quest-progress</c> (NEO-119).</summary>
public static class QuestProgressApi
{
public const string StatusNotStarted = "not_started";
public const string StatusActive = "active";
public const string StatusCompleted = "completed";
public static WebApplication MapQuestProgressApi(this WebApplication app)
{
app.MapGet(
"/game/players/{id}/quest-progress",
(string id, IPositionStateStore positions, IQuestDefinitionRegistry questRegistry,
IPlayerQuestStateStore progressStore, IPlayerInventoryStore inventoryStore,
IItemDefinitionRegistry itemRegistry, ISkillDefinitionRegistry skillRegistry,
IPlayerSkillProgressionStore skillProgressionStore, ISkillLevelCurve levelCurve,
PerkUnlockEngine perkUnlockEngine, IPlayerPerkStateStore perkStore,
IRewardDeliveryStore deliveryStore, TimeProvider timeProvider, ILogger<Program>? logger) =>
{
var trimmedId = id.Trim();
if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
{
return Results.NotFound();
}
QuestObjectiveWiring.TryRefreshInventoryProgressForPlayer(
trimmedId,
questRegistry,
progressStore,
inventoryStore,
itemRegistry,
skillRegistry,
skillProgressionStore,
levelCurve,
perkUnlockEngine,
perkStore,
deliveryStore,
timeProvider,
logger);
return Results.Json(BuildSnapshot(trimmedId, questRegistry, progressStore, deliveryStore));
});
return app;
}
internal static QuestProgressListResponse BuildSnapshot(
string playerId,
IQuestDefinitionRegistry questRegistry,
IPlayerQuestStateStore progressStore,
IRewardDeliveryStore deliveryStore)
{
var defs = questRegistry.GetDefinitionsInIdOrder();
var rows = new List<QuestProgressRowJson>(defs.Count);
foreach (var def in defs)
{
rows.Add(MapQuestProgressRow(playerId, def.Id, progressStore, deliveryStore));
}
return new QuestProgressListResponse
{
PlayerId = playerId,
Quests = rows,
SchemaVersion = QuestProgressListResponse.CurrentSchemaVersion,
};
}
internal static QuestProgressRowJson MapQuestProgressRow(
string playerId,
string questId,
IPlayerQuestStateStore progressStore,
IRewardDeliveryStore deliveryStore)
{
if (!progressStore.TryGetProgress(playerId, questId, out var snapshot))
{
return NotStartedRow(questId);
}
return MapQuestProgressRow(playerId, snapshot, deliveryStore);
}
internal static QuestProgressRowJson MapQuestProgressRow(
string playerId,
QuestStepState snapshot,
IRewardDeliveryStore deliveryStore)
{
var counters = new Dictionary<string, int>(snapshot.ObjectiveCounters, StringComparer.Ordinal);
return snapshot.Status switch
{
QuestProgressStatus.Active => new QuestProgressRowJson
{
QuestId = snapshot.QuestId,
Status = StatusActive,
CurrentStepIndex = snapshot.CurrentStepIndex,
ObjectiveCounters = counters,
},
QuestProgressStatus.Completed => new QuestProgressRowJson
{
QuestId = snapshot.QuestId,
Status = StatusCompleted,
CurrentStepIndex = snapshot.CurrentStepIndex,
ObjectiveCounters = counters,
CompletedAt = snapshot.CompletedAt,
CompletionRewardSummary = MapCompletionRewardSummary(playerId, snapshot.QuestId, deliveryStore),
},
_ => throw new InvalidOperationException($"Unknown quest progress status '{snapshot.Status}'."),
};
}
private static QuestCompletionRewardSummaryJson? MapCompletionRewardSummary(
string playerId,
string questId,
IRewardDeliveryStore deliveryStore)
{
if (!deliveryStore.TryGet(playerId, questId, out var deliveryEvent))
{
return null;
}
return new QuestCompletionRewardSummaryJson
{
ItemGrants = MapItemGrants(deliveryEvent.GrantedItems),
SkillXpGrants = MapSkillXpGrants(deliveryEvent.GrantedSkillXp),
};
}
/// <summary>Preserves commit-time grant order from <see cref="RewardDeliveryEvent.GrantedItems"/>.</summary>
private static List<QuestItemGrantJson> MapItemGrants(IReadOnlyList<RewardItemGrantApplied> grants)
{
var summary = new List<QuestItemGrantJson>(grants.Count);
foreach (var grant in grants)
{
summary.Add(
new QuestItemGrantJson
{
ItemId = grant.ItemId,
Quantity = grant.Quantity,
});
}
return summary;
}
/// <summary>Preserves commit-time grant order from <see cref="RewardDeliveryEvent.GrantedSkillXp"/>.</summary>
private static List<QuestSkillXpGrantJson> MapSkillXpGrants(IReadOnlyList<RewardSkillXpGrantApplied> grants)
{
var summary = new List<QuestSkillXpGrantJson>(grants.Count);
foreach (var grant in grants)
{
summary.Add(
new QuestSkillXpGrantJson
{
SkillId = grant.SkillId,
Amount = grant.Amount,
});
}
return summary;
}
private static QuestProgressRowJson NotStartedRow(string questId) =>
new()
{
QuestId = questId,
Status = StatusNotStarted,
CurrentStepIndex = 0,
ObjectiveCounters = new Dictionary<string, int>(StringComparer.Ordinal),
};
}