177 lines
6.3 KiB
C#
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),
|
|
};
|
|
}
|