diff --git a/bruno/neon-sprawl-server/craft/Post craft gather refine make spine.bru b/bruno/neon-sprawl-server/craft/Post craft gather refine make spine.bru index 4fe7cf6..c42d955 100644 --- a/bruno/neon-sprawl-server/craft/Post craft gather refine make spine.bru +++ b/bruno/neon-sprawl-server/craft/Post craft gather refine make spine.bru @@ -5,7 +5,7 @@ meta { } docs { - NEO-70 prototype spine: accumulate >=11 scrap_metal_bulk (gather when nodes allow, reuse persisted inventory after dotnet test), refine_scrap_efficient when refined_plate_stock < 2, then make_field_stim_mk0. NEO-118: best-effort quest objective wiring on craft success (active craft_recipe quests). No inventory POST shortcuts for spine materials. + NEO-70 prototype spine: accumulate >=11 scrap_metal_bulk (gather when nodes allow, reuse persisted inventory after dotnet test), refine_scrap_efficient when refined_plate_stock < 2, then make_field_stim_mk0. NEO-118: best-effort quest objective wiring on craft success (active craft_recipe quests). Deny envelopes return empty inputsConsumed/outputsGranted arrays. No inventory POST shortcuts for spine materials. } script:pre-request { diff --git a/server/NeonSprawl.Server.Tests/Game/Crafting/PlayerCraftApiTests.cs b/server/NeonSprawl.Server.Tests/Game/Crafting/PlayerCraftApiTests.cs index 5838719..3488291 100644 --- a/server/NeonSprawl.Server.Tests/Game/Crafting/PlayerCraftApiTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Crafting/PlayerCraftApiTests.cs @@ -266,6 +266,28 @@ public sealed class PlayerCraftApiTests Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); } + [Fact] + public void MapResponse_ShouldReturnEmptyIoArrays_WhenCraftDenied() + { + // Arrange + var result = new CraftResult( + Success: false, + ReasonCode: CraftReasonCodes.UnknownRecipe, + InputsConsumed: [], + OutputsGranted: [], + XpGrantSummary: null); + + // Act + var response = PlayerCraftApi.MapResponse(result); + + // Assert + Assert.False(response.Success); + Assert.Equal(CraftReasonCodes.UnknownRecipe, response.ReasonCode); + Assert.Empty(response.InputsConsumed); + Assert.Empty(response.OutputsGranted); + Assert.Null(response.XpGrantSummary); + } + private static void SeedStack(InMemoryWebApplicationFactory factory, string itemId, int quantity) { var itemRegistry = factory.Services.GetRequiredService(); diff --git a/server/NeonSprawl.Server/Game/Crafting/CraftResult.cs b/server/NeonSprawl.Server/Game/Crafting/CraftResult.cs index fe69884..720756c 100644 --- a/server/NeonSprawl.Server/Game/Crafting/CraftResult.cs +++ b/server/NeonSprawl.Server/Game/Crafting/CraftResult.cs @@ -4,12 +4,12 @@ namespace NeonSprawl.Server.Game.Crafting; /// Server-internal craft resolution envelope (NEO-69); promoted to wire JSON via (NEO-70). /// NEO-71 telemetry hook sites live in (success path + centralized Deny helper). /// -/// Scaled inputs removed on success; empty when denied. -/// Scaled outputs added on success; empty when denied. -/// Refine skill XP applied on success; null when denied. public readonly record struct CraftResult( bool Success, string? ReasonCode, + /// Scaled inputs removed on success; empty when denied. IReadOnlyList InputsConsumed, + /// Scaled outputs added on success; empty when denied. IReadOnlyList OutputsGranted, + /// Refine skill XP applied on success; null when denied. CraftXpGrantSummary? XpGrantSummary); diff --git a/server/NeonSprawl.Server/Game/Crafting/PlayerCraftApi.cs b/server/NeonSprawl.Server/Game/Crafting/PlayerCraftApi.cs index a7ce75a..009e9b5 100644 --- a/server/NeonSprawl.Server/Game/Crafting/PlayerCraftApi.cs +++ b/server/NeonSprawl.Server/Game/Crafting/PlayerCraftApi.cs @@ -96,11 +96,11 @@ public static class PlayerCraftApi }; } - private static IReadOnlyList MapIoRows(IReadOnlyList rows) + private static CraftIoAppliedJson[] MapIoRows(IReadOnlyList rows) { if (rows.Count == 0) { - return Array.Empty(); + return []; } var mapped = new CraftIoAppliedJson[rows.Count]; diff --git a/server/NeonSprawl.Server/Game/Encounters/EncounterCombatWiring.cs b/server/NeonSprawl.Server/Game/Encounters/EncounterCombatWiring.cs index 7f4c3df..b173c07 100644 --- a/server/NeonSprawl.Server/Game/Encounters/EncounterCombatWiring.cs +++ b/server/NeonSprawl.Server/Game/Encounters/EncounterCombatWiring.cs @@ -116,7 +116,7 @@ public static class EncounterCombatWiring if (!completion.Success) { - if (logger?.IsEnabled(LogLevel.Debug) == true) + if (logger?.IsEnabled(LogLevel.Debug) is true) { logger.LogDebug( "Encounter completion grant skipped for player {PlayerId} encounter {EncounterId} ({ReasonCode})",