neon-sprawl/server/NeonSprawl.Server/Game/Crafting/PlayerCraftApi.cs

120 lines
4.1 KiB
C#

using NeonSprawl.Server.Game.Factions;
using NeonSprawl.Server.Game.Items;
using NeonSprawl.Server.Game.Mastery;
using NeonSprawl.Server.Game.PositionState;
using NeonSprawl.Server.Game.Quests;
using NeonSprawl.Server.Game.Rewards;
using NeonSprawl.Server.Game.Skills;
namespace NeonSprawl.Server.Game.Crafting;
/// <summary>Maps <c>POST /game/players/{{id}}/craft</c> — craft execution (NEO-70).</summary>
public static class PlayerCraftApi
{
public static WebApplication MapPlayerCraftApi(this WebApplication app)
{
app.MapPost(
"/game/players/{id}/craft",
(string id, PlayerCraftRequest? body, IPositionStateStore positions,
IRecipeDefinitionRegistry recipeRegistry, IItemDefinitionRegistry itemRegistry,
IPlayerInventoryStore inventoryStore, ISkillDefinitionRegistry skillRegistry,
IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve,
PerkUnlockEngine perkUnlockEngine, IQuestDefinitionRegistry questRegistry,
IPlayerQuestStateStore progressStore, IPlayerPerkStateStore perkStore,
IRewardDeliveryStore deliveryStore, IFactionStandingStore standingStore,
IReputationDeltaStore auditStore, TimeProvider timeProvider) =>
{
if (body is null || body.SchemaVersion != PlayerCraftRequest.CurrentSchemaVersion)
{
return Results.BadRequest();
}
if (body.RecipeId is null || body.RecipeId.Trim().Length == 0)
{
return Results.BadRequest();
}
var trimmedId = id.Trim();
if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
{
return Results.NotFound();
}
var quantity = body.Quantity ?? 1;
var result = CraftOperations.TryCraft(
trimmedId,
body.RecipeId,
quantity,
recipeRegistry,
itemRegistry,
inventoryStore,
skillRegistry,
xpStore,
levelCurve,
perkUnlockEngine,
questRegistry,
progressStore,
perkStore,
deliveryStore,
standingStore,
auditStore,
timeProvider);
if (!result.Success &&
(string.Equals(result.ReasonCode, CraftReasonCodes.InventoryStoreMissing, StringComparison.Ordinal) ||
string.Equals(result.ReasonCode, CraftReasonCodes.ProgressionStoreMissing, StringComparison.Ordinal)))
{
return Results.NotFound();
}
return Results.Json(MapResponse(result));
});
return app;
}
internal static CraftResponse MapResponse(CraftResult result)
{
CraftXpGrantSummaryJson? xp = null;
if (result.XpGrantSummary is { } summary)
{
xp = new CraftXpGrantSummaryJson
{
SkillId = summary.SkillId,
Amount = summary.Amount,
SourceKind = summary.SourceKind,
};
}
return new CraftResponse
{
Success = result.Success,
ReasonCode = result.ReasonCode,
InputsConsumed = MapIoRows(result.InputsConsumed),
OutputsGranted = MapIoRows(result.OutputsGranted),
XpGrantSummary = xp,
};
}
private static CraftIoAppliedJson[] MapIoRows(IReadOnlyList<CraftIoApplied> rows)
{
if (rows.Count == 0)
{
return [];
}
var mapped = new CraftIoAppliedJson[rows.Count];
for (var i = 0; i < rows.Count; i++)
{
var row = rows[i];
mapped[i] = new CraftIoAppliedJson
{
ItemId = row.ItemId,
Quantity = row.Quantity,
};
}
return mapped;
}
}