NEO-128: wire quest complete to reward router deliver-then-mark.
TryMarkComplete delivers completionRewardBundle before marking complete; objective wiring and upstream ops thread reward dependencies. Tests cover bundle delivery, idempotent replay, and inventory-full deny.pull/167/head
parent
7abe7dcafd
commit
da99769ddf
|
|
@ -0,0 +1,28 @@
|
||||||
|
meta {
|
||||||
|
name: GET health (quest complete reward wiring NEO-128)
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{baseUrl}}/health
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
|
|
||||||
|
docs {
|
||||||
|
NEO-128 wires RewardRouterOperations into QuestStateOperations.TryMarkComplete (deliver-then-mark).
|
||||||
|
No new HTTP routes — quest-progress GET/accept POST signatures gained reward-router DI only.
|
||||||
|
Use this request to confirm the host started after wiring; verify bundle delivery via dotnet test
|
||||||
|
(QuestStateOperationsTests, QuestObjectiveWiringTests).
|
||||||
|
}
|
||||||
|
|
||||||
|
tests {
|
||||||
|
test("status 200", function () {
|
||||||
|
expect(res.getStatus()).to.equal(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("service identity", function () {
|
||||||
|
expect(res.getBody().service).to.equal("NeonSprawl.Server");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -71,3 +71,5 @@ Epic 7 **Slice 2** — `reward_delivery`, `unlock_granted`; no double-claim on r
|
||||||
**NEO-126 (E7M2-03 delivery store):** Quest completion reward delivery uses **`IRewardDeliveryStore`** + **`InMemoryRewardDeliveryStore`** in `server/NeonSprawl.Server/Game/Rewards/` — idempotent **`RewardDeliveryEvent`** per player+quest (idempotency key **`{playerId}:quest_complete:{questId}`**; grant snapshots for item + skill XP rows). Quest-state wiring lands in NEO-128; HTTP **`completionRewardSummary`** in NEO-129. See [NEO-126 implementation plan](../../plans/NEO-126-implementation-plan.md); [server README — Reward delivery store (NEO-126)](../../../server/README.md#reward-delivery-store-neo-126).
|
**NEO-126 (E7M2-03 delivery store):** Quest completion reward delivery uses **`IRewardDeliveryStore`** + **`InMemoryRewardDeliveryStore`** in `server/NeonSprawl.Server/Game/Rewards/` — idempotent **`RewardDeliveryEvent`** per player+quest (idempotency key **`{playerId}:quest_complete:{questId}`**; grant snapshots for item + skill XP rows). Quest-state wiring lands in NEO-128; HTTP **`completionRewardSummary`** in NEO-129. See [NEO-126 implementation plan](../../plans/NEO-126-implementation-plan.md); [server README — Reward delivery store (NEO-126)](../../../server/README.md#reward-delivery-store-neo-126).
|
||||||
|
|
||||||
**NEO-127 (E7M2-04 router apply):** **`RewardRouterOperations.TryDeliverQuestCompletion`** in `server/NeonSprawl.Server/Game/Rewards/` applies **`QuestRewardBundleRow`** item grants via **`PlayerInventoryOperations`** and skill XP via **`SkillProgressionGrantOperations.TryApplyGrant`** (`mission_reward` only), with compensating item rollback on partial failure, then **`IRewardDeliveryStore.TryRecord`**. See [NEO-127 implementation plan](../../plans/NEO-127-implementation-plan.md); [server README — Reward router (NEO-127)](../../../server/README.md#reward-router-neo-127).
|
**NEO-127 (E7M2-04 router apply):** **`RewardRouterOperations.TryDeliverQuestCompletion`** in `server/NeonSprawl.Server/Game/Rewards/` applies **`QuestRewardBundleRow`** item grants via **`PlayerInventoryOperations`** and skill XP via **`SkillProgressionGrantOperations.TryApplyGrant`** (`mission_reward` only), with compensating item rollback on partial failure, then **`IRewardDeliveryStore.TryRecord`**. See [NEO-127 implementation plan](../../plans/NEO-127-implementation-plan.md); [server README — Reward router (NEO-127)](../../../server/README.md#reward-router-neo-127).
|
||||||
|
|
||||||
|
**NEO-128 (E7M2-05 quest-state wiring):** **`QuestStateOperations.TryMarkComplete`** calls **`RewardRouterOperations.TryDeliverQuestCompletion`** on the first-time completion path (deliver-then-mark); idempotent **`completed`** replay skips the router. **`QuestObjectiveWiring`** threads reward dependencies through gather/craft/encounter/GET refresh into the same path. HTTP **`completionRewardSummary`** in NEO-129. See [NEO-128 implementation plan](../../plans/NEO-128-implementation-plan.md); [server README — Quest state operations (NEO-117)](../../../server/README.md#quest-state-operations-neo-117).
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -194,8 +194,8 @@ Combat intro bundle is **skill XP only** — encounter loot already granted **`c
|
||||||
|
|
||||||
**Acceptance criteria**
|
**Acceptance criteria**
|
||||||
|
|
||||||
- [ ] Completing a quest via objective wiring delivers bundle once.
|
- [x] Completing a quest via objective wiring delivers bundle once.
|
||||||
- [ ] Idempotent complete does not double-grant items or XP.
|
- [x] Idempotent complete does not double-grant items or XP.
|
||||||
|
|
||||||
**Client counterpart:** [NEO-131](https://linear.app/neon-sprawl/issue/NEO-131) — HUD surfacing (blocked by E7M2-06).
|
**Client counterpart:** [NEO-131](https://linear.app/neon-sprawl/issue/NEO-131) — HUD surfacing (blocked by E7M2-06).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,15 @@
|
||||||
|
|
||||||
## Acceptance criteria checklist
|
## Acceptance criteria checklist
|
||||||
|
|
||||||
- [ ] Completing a quest via objective wiring delivers bundle once.
|
- [x] Completing a quest via objective wiring delivers bundle once.
|
||||||
- [ ] Idempotent complete replay does not double-grant items or XP.
|
- [x] Idempotent complete replay does not double-grant items or XP.
|
||||||
|
|
||||||
|
## Implementation reconciliation (shipped)
|
||||||
|
|
||||||
|
- **Operations:** `QuestStateOperations.TryMarkComplete` — deliver-then-mark via `RewardRouterOperations`; idempotent `completed` skip; deny passthrough from `RewardDeliveryReasonCodes`.
|
||||||
|
- **Wiring:** `QuestObjectiveWiring` + `GatherOperations` / `CraftOperations` / `EncounterCompletionOperations` / quest HTTP APIs thread reward-router dependencies.
|
||||||
|
- **Tests:** `QuestStateOperationsTests` — bundle delivery, idempotent replay, inventory-full deny; `QuestObjectiveWiringTests` — gather-intro wiring + replay no double-grant.
|
||||||
|
- **Docs:** `server/README.md`; E7.M2 module anchor + alignment register updated.
|
||||||
|
|
||||||
## Technical approach
|
## Technical approach
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using NeonSprawl.Server.Game.Crafting;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -35,6 +36,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -79,6 +82,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -114,6 +119,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -151,6 +158,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -185,6 +194,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -220,6 +231,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -252,6 +265,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -280,6 +295,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -311,6 +328,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -344,6 +363,8 @@ public sealed class CraftOperationsTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -392,7 +413,9 @@ public sealed class CraftOperationsTests
|
||||||
services.GetRequiredService<ISkillLevelCurve>(),
|
services.GetRequiredService<ISkillLevelCurve>(),
|
||||||
services.GetRequiredService<PerkUnlockEngine>(),
|
services.GetRequiredService<PerkUnlockEngine>(),
|
||||||
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
||||||
services.GetRequiredService<IPlayerQuestStateStore>());
|
services.GetRequiredService<IPlayerQuestStateStore>(),
|
||||||
|
services.GetRequiredService<IPlayerPerkStateStore>(),
|
||||||
|
services.GetRequiredService<IRewardDeliveryStore>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CountBagItem(PlayerInventorySnapshot snapshot, string itemId)
|
private static int CountBagItem(PlayerInventorySnapshot snapshot, string itemId)
|
||||||
|
|
@ -429,7 +452,9 @@ public sealed class CraftOperationsTests
|
||||||
ISkillLevelCurve LevelCurve,
|
ISkillLevelCurve LevelCurve,
|
||||||
PerkUnlockEngine PerkUnlockEngine,
|
PerkUnlockEngine PerkUnlockEngine,
|
||||||
IQuestDefinitionRegistry QuestRegistry,
|
IQuestDefinitionRegistry QuestRegistry,
|
||||||
IPlayerQuestStateStore ProgressStore);
|
IPlayerQuestStateStore ProgressStore,
|
||||||
|
IPlayerPerkStateStore PerkStore,
|
||||||
|
IRewardDeliveryStore DeliveryStore);
|
||||||
|
|
||||||
private sealed class ProgressionStoreAlwaysMissing : IPlayerSkillProgressionStore
|
private sealed class ProgressionStoreAlwaysMissing : IPlayerSkillProgressionStore
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NeonSprawl.Server.Game.Encounters;
|
using NeonSprawl.Server.Game.Encounters;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -37,6 +40,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -68,6 +77,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -103,6 +118,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -136,6 +157,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -162,6 +189,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
deps.InventoryStore.TryGetSnapshot(PlayerId, out var before);
|
deps.InventoryStore.TryGetSnapshot(PlayerId, out var before);
|
||||||
|
|
||||||
|
|
@ -180,6 +213,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -212,6 +251,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -253,6 +298,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -291,6 +342,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -313,6 +370,12 @@ public sealed class EncounterCombatWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,7 +426,13 @@ public sealed class EncounterCombatWiringTests
|
||||||
services.GetRequiredService<IItemDefinitionRegistry>(),
|
services.GetRequiredService<IItemDefinitionRegistry>(),
|
||||||
services.GetRequiredService<IPlayerInventoryStore>(),
|
services.GetRequiredService<IPlayerInventoryStore>(),
|
||||||
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
||||||
services.GetRequiredService<IPlayerQuestStateStore>());
|
services.GetRequiredService<IPlayerQuestStateStore>(),
|
||||||
|
services.GetRequiredService<ISkillDefinitionRegistry>(),
|
||||||
|
services.GetRequiredService<IPlayerSkillProgressionStore>(),
|
||||||
|
services.GetRequiredService<ISkillLevelCurve>(),
|
||||||
|
services.GetRequiredService<PerkUnlockEngine>(),
|
||||||
|
services.GetRequiredService<IPlayerPerkStateStore>(),
|
||||||
|
services.GetRequiredService<IRewardDeliveryStore>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CountBagItem(PlayerInventorySnapshot snapshot, string itemId)
|
private static int CountBagItem(PlayerInventorySnapshot snapshot, string itemId)
|
||||||
|
|
@ -389,5 +458,11 @@ public sealed class EncounterCombatWiringTests
|
||||||
IItemDefinitionRegistry ItemRegistry,
|
IItemDefinitionRegistry ItemRegistry,
|
||||||
IPlayerInventoryStore InventoryStore,
|
IPlayerInventoryStore InventoryStore,
|
||||||
IQuestDefinitionRegistry QuestRegistry,
|
IQuestDefinitionRegistry QuestRegistry,
|
||||||
IPlayerQuestStateStore QuestProgressStore);
|
IPlayerQuestStateStore QuestProgressStore,
|
||||||
|
ISkillDefinitionRegistry SkillRegistry,
|
||||||
|
IPlayerSkillProgressionStore SkillProgressionStore,
|
||||||
|
ISkillLevelCurve LevelCurve,
|
||||||
|
PerkUnlockEngine PerkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore PerkStore,
|
||||||
|
IRewardDeliveryStore DeliveryStore);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NeonSprawl.Server.Game.Encounters;
|
using NeonSprawl.Server.Game.Encounters;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -37,6 +40,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -80,6 +89,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterFirst);
|
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterFirst);
|
||||||
|
|
||||||
|
|
@ -96,6 +111,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -154,6 +175,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -188,6 +215,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -224,6 +257,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -258,6 +297,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -290,6 +335,12 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.QuestProgressStore,
|
deps.QuestProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -352,7 +403,13 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
services.GetRequiredService<IItemDefinitionRegistry>(),
|
services.GetRequiredService<IItemDefinitionRegistry>(),
|
||||||
services.GetRequiredService<IPlayerInventoryStore>(),
|
services.GetRequiredService<IPlayerInventoryStore>(),
|
||||||
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
||||||
services.GetRequiredService<IPlayerQuestStateStore>());
|
services.GetRequiredService<IPlayerQuestStateStore>(),
|
||||||
|
services.GetRequiredService<ISkillDefinitionRegistry>(),
|
||||||
|
services.GetRequiredService<IPlayerSkillProgressionStore>(),
|
||||||
|
services.GetRequiredService<ISkillLevelCurve>(),
|
||||||
|
services.GetRequiredService<PerkUnlockEngine>(),
|
||||||
|
services.GetRequiredService<IPlayerPerkStateStore>(),
|
||||||
|
services.GetRequiredService<IRewardDeliveryStore>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CountBagItem(PlayerInventorySnapshot snapshot, string itemId)
|
private static int CountBagItem(PlayerInventorySnapshot snapshot, string itemId)
|
||||||
|
|
@ -392,7 +449,13 @@ public sealed class EncounterCompletionOperationsTests
|
||||||
IItemDefinitionRegistry ItemRegistry,
|
IItemDefinitionRegistry ItemRegistry,
|
||||||
IPlayerInventoryStore InventoryStore,
|
IPlayerInventoryStore InventoryStore,
|
||||||
IQuestDefinitionRegistry QuestRegistry,
|
IQuestDefinitionRegistry QuestRegistry,
|
||||||
IPlayerQuestStateStore QuestProgressStore);
|
IPlayerQuestStateStore QuestProgressStore,
|
||||||
|
ISkillDefinitionRegistry SkillRegistry,
|
||||||
|
IPlayerSkillProgressionStore SkillProgressionStore,
|
||||||
|
ISkillLevelCurve LevelCurve,
|
||||||
|
PerkUnlockEngine PerkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore PerkStore,
|
||||||
|
IRewardDeliveryStore DeliveryStore);
|
||||||
|
|
||||||
private sealed class CompletionStoreDeniesMark(IEncounterCompletionStore inner) : IEncounterCompletionStore
|
private sealed class CompletionStoreDeniesMark(IEncounterCompletionStore inner) : IEncounterCompletionStore
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using NeonSprawl.Server.Game.Gathering;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -36,6 +37,8 @@ public sealed class GatherOperationsTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
deps.InventoryStore.TryGetSnapshot(PlayerId, out var inventory);
|
deps.InventoryStore.TryGetSnapshot(PlayerId, out var inventory);
|
||||||
|
|
@ -90,6 +93,8 @@ public sealed class GatherOperationsTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterInventory);
|
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterInventory);
|
||||||
|
|
@ -126,6 +131,8 @@ public sealed class GatherOperationsTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
Assert.True(gather.Success);
|
Assert.True(gather.Success);
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +154,8 @@ public sealed class GatherOperationsTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterInventory);
|
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterInventory);
|
||||||
|
|
@ -182,6 +191,8 @@ public sealed class GatherOperationsTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
var rowExists = deps.InstanceStore.TryGetRemainingGathers(unknownId, out _);
|
var rowExists = deps.InstanceStore.TryGetRemainingGathers(unknownId, out _);
|
||||||
|
|
@ -215,6 +226,8 @@ public sealed class GatherOperationsTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterInventory);
|
deps.InventoryStore.TryGetSnapshot(PlayerId, out var afterInventory);
|
||||||
|
|
@ -249,6 +262,8 @@ public sealed class GatherOperationsTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
|
|
||||||
deps.InventoryStore.TryGetSnapshot(PlayerId, out var inventory);
|
deps.InventoryStore.TryGetSnapshot(PlayerId, out var inventory);
|
||||||
|
|
@ -273,7 +288,9 @@ public sealed class GatherOperationsTests
|
||||||
services.GetRequiredService<PerkUnlockEngine>(),
|
services.GetRequiredService<PerkUnlockEngine>(),
|
||||||
services.GetRequiredService<IResourceNodeInstanceStore>(),
|
services.GetRequiredService<IResourceNodeInstanceStore>(),
|
||||||
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
services.GetRequiredService<IQuestDefinitionRegistry>(),
|
||||||
services.GetRequiredService<IPlayerQuestStateStore>());
|
services.GetRequiredService<IPlayerQuestStateStore>(),
|
||||||
|
services.GetRequiredService<IPlayerPerkStateStore>(),
|
||||||
|
services.GetRequiredService<IRewardDeliveryStore>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int CountItem(PlayerInventorySnapshot snapshot, string itemId)
|
private static int CountItem(PlayerInventorySnapshot snapshot, string itemId)
|
||||||
|
|
@ -311,7 +328,9 @@ public sealed class GatherOperationsTests
|
||||||
PerkUnlockEngine PerkUnlockEngine,
|
PerkUnlockEngine PerkUnlockEngine,
|
||||||
IResourceNodeInstanceStore InstanceStore,
|
IResourceNodeInstanceStore InstanceStore,
|
||||||
IQuestDefinitionRegistry QuestRegistry,
|
IQuestDefinitionRegistry QuestRegistry,
|
||||||
IPlayerQuestStateStore ProgressStore);
|
IPlayerQuestStateStore ProgressStore,
|
||||||
|
IPlayerPerkStateStore PerkStore,
|
||||||
|
IRewardDeliveryStore DeliveryStore);
|
||||||
|
|
||||||
private sealed class ProgressionStoreAlwaysMissing : IPlayerSkillProgressionStore
|
private sealed class ProgressionStoreAlwaysMissing : IPlayerSkillProgressionStore
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,10 @@ using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -202,22 +205,45 @@ public sealed class QuestAcceptApiTests
|
||||||
// Arrange
|
// Arrange
|
||||||
await using var factory = new InMemoryWebApplicationFactory();
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
var questRegistry = factory.Services.GetRequiredService<IQuestDefinitionRegistry>();
|
var services = factory.Services;
|
||||||
var progressStore = factory.Services.GetRequiredService<IPlayerQuestStateStore>();
|
var questRegistry = services.GetRequiredService<IQuestDefinitionRegistry>();
|
||||||
|
var progressStore = services.GetRequiredService<IPlayerQuestStateStore>();
|
||||||
|
var inventoryStore = services.GetRequiredService<IPlayerInventoryStore>();
|
||||||
|
var itemRegistry = services.GetRequiredService<IItemDefinitionRegistry>();
|
||||||
|
var skillRegistry = services.GetRequiredService<ISkillDefinitionRegistry>();
|
||||||
|
var skillProgressionStore = services.GetRequiredService<IPlayerSkillProgressionStore>();
|
||||||
|
var levelCurve = services.GetRequiredService<ISkillLevelCurve>();
|
||||||
|
var perkUnlockEngine = services.GetRequiredService<PerkUnlockEngine>();
|
||||||
|
var perkStore = services.GetRequiredService<IPlayerPerkStateStore>();
|
||||||
|
var deliveryStore = services.GetRequiredService<IRewardDeliveryStore>();
|
||||||
var timeProvider = TimeProvider.System;
|
var timeProvider = TimeProvider.System;
|
||||||
Assert.True(QuestStateOperations.TryAccept(
|
Assert.True(QuestStateOperations.TryAccept(
|
||||||
PlayerId,
|
PlayerId,
|
||||||
GatherQuestId,
|
GatherQuestId,
|
||||||
questRegistry,
|
questRegistry,
|
||||||
progressStore,
|
progressStore,
|
||||||
factory.Services.GetRequiredService<IPlayerInventoryStore>(),
|
inventoryStore,
|
||||||
factory.Services.GetRequiredService<IItemDefinitionRegistry>(),
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider).Success);
|
timeProvider).Success);
|
||||||
Assert.True(QuestStateOperations.TryMarkComplete(
|
Assert.True(QuestStateOperations.TryMarkComplete(
|
||||||
PlayerId,
|
PlayerId,
|
||||||
GatherQuestId,
|
GatherQuestId,
|
||||||
questRegistry,
|
questRegistry,
|
||||||
progressStore,
|
progressStore,
|
||||||
|
itemRegistry,
|
||||||
|
inventoryStore,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider).Success);
|
timeProvider).Success);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using NeonSprawl.Server.Game.Gathering;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -41,6 +42,37 @@ public sealed class QuestObjectiveWiringTests
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(deps.ProgressStore.TryGetProgress(PlayerId, GatherQuestId, out var progress));
|
Assert.True(deps.ProgressStore.TryGetProgress(PlayerId, GatherQuestId, out var progress));
|
||||||
Assert.Equal(QuestProgressStatus.Completed, progress.Status);
|
Assert.Equal(QuestProgressStatus.Completed, progress.Status);
|
||||||
|
Assert.Equal(
|
||||||
|
3 * GatherSkillXpConstants.ActivityXpPerResourceNodeGather + 25,
|
||||||
|
deps.XpStore.GetXpTotals(PlayerId).GetValueOrDefault("salvage"));
|
||||||
|
Assert.True(deps.DeliveryStore.TryGet(PlayerId, GatherQuestId, out var deliveryEvent));
|
||||||
|
Assert.Single(deliveryEvent.GrantedSkillXp);
|
||||||
|
Assert.Equal(25, deliveryEvent.GrantedSkillXp[0].Amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GatherIntro_ShouldNotDoubleGrantXp_WhenCompleteReplayed()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
|
var deps = ResolveDependencies(factory);
|
||||||
|
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
Assert.True(TryGather(deps, AlphaNodeId).Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.True(deps.DeliveryStore.TryGet(PlayerId, GatherQuestId, out var firstEvent));
|
||||||
|
var salvageXpBeforeReplay = deps.XpStore.GetXpTotals(PlayerId).GetValueOrDefault("salvage");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var replay = TryMarkComplete(deps, GatherQuestId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(replay.Success);
|
||||||
|
Assert.Equal(salvageXpBeforeReplay, deps.XpStore.GetXpTotals(PlayerId).GetValueOrDefault("salvage"));
|
||||||
|
Assert.True(deps.DeliveryStore.TryGet(PlayerId, GatherQuestId, out var replayEvent));
|
||||||
|
Assert.Equal(firstEvent.IdempotencyKey, replayEvent.IdempotencyKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -78,6 +110,12 @@ public sealed class QuestObjectiveWiringTests
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.ItemRegistry,
|
deps.ItemRegistry,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -147,37 +185,13 @@ public sealed class QuestObjectiveWiringTests
|
||||||
SeedStack(deps, PrototypeE7M1QuestCatalogRules.ChainTerminalItemId, 1);
|
SeedStack(deps, PrototypeE7M1QuestCatalogRules.ChainTerminalItemId, 1);
|
||||||
Assert.True(TryAccept(deps, ChainQuestId).Success);
|
Assert.True(TryAccept(deps, ChainQuestId).Success);
|
||||||
Assert.True(deps.ProgressStore.TryUpdateObjectiveCounter(PlayerId, ChainQuestId, ChainGatherObjectiveId, 5, out _));
|
Assert.True(deps.ProgressStore.TryUpdateObjectiveCounter(PlayerId, ChainQuestId, ChainGatherObjectiveId, 5, out _));
|
||||||
Assert.True(QuestStateOperations.TryAdvanceStep(
|
Assert.True(TryAdvanceStep(deps, ChainQuestId, 1).Success);
|
||||||
PlayerId,
|
|
||||||
ChainQuestId,
|
|
||||||
1,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
deps.InventoryStore,
|
|
||||||
deps.ItemRegistry,
|
|
||||||
deps.TimeProvider).Success);
|
|
||||||
Assert.True(deps.ProgressStore.TryUpdateObjectiveCounter(PlayerId, ChainQuestId, "chain_obj_refine", 1, out _));
|
Assert.True(deps.ProgressStore.TryUpdateObjectiveCounter(PlayerId, ChainQuestId, "chain_obj_refine", 1, out _));
|
||||||
Assert.True(QuestStateOperations.TryAdvanceStep(
|
Assert.True(TryAdvanceStep(deps, ChainQuestId, 2).Success);
|
||||||
PlayerId,
|
|
||||||
ChainQuestId,
|
|
||||||
2,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
deps.InventoryStore,
|
|
||||||
deps.ItemRegistry,
|
|
||||||
deps.TimeProvider).Success);
|
|
||||||
Assert.True(deps.ProgressStore.TryUpdateObjectiveCounter(PlayerId, ChainQuestId, "chain_obj_stim", 1, out _));
|
Assert.True(deps.ProgressStore.TryUpdateObjectiveCounter(PlayerId, ChainQuestId, "chain_obj_stim", 1, out _));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var advance = QuestStateOperations.TryAdvanceStep(
|
var advance = TryAdvanceStep(deps, ChainQuestId, 3);
|
||||||
PlayerId,
|
|
||||||
ChainQuestId,
|
|
||||||
3,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
deps.InventoryStore,
|
|
||||||
deps.ItemRegistry,
|
|
||||||
deps.TimeProvider);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(advance.Success);
|
Assert.True(advance.Success);
|
||||||
|
|
@ -201,6 +215,12 @@ public sealed class QuestObjectiveWiringTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
Assert.True(result.Success);
|
Assert.True(result.Success);
|
||||||
}
|
}
|
||||||
|
|
@ -232,17 +252,51 @@ public sealed class QuestObjectiveWiringTests
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.ItemRegistry,
|
deps.ItemRegistry,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
|
deps.TimeProvider);
|
||||||
|
|
||||||
|
private static QuestStateOperationResult TryAdvanceStep(QuestWiringTestDependencies deps, string questId, int step) =>
|
||||||
|
QuestStateOperations.TryAdvanceStep(
|
||||||
|
PlayerId,
|
||||||
|
questId,
|
||||||
|
step,
|
||||||
|
deps.QuestRegistry,
|
||||||
|
deps.ProgressStore,
|
||||||
|
deps.InventoryStore,
|
||||||
|
deps.ItemRegistry,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
|
deps.TimeProvider);
|
||||||
|
|
||||||
|
private static QuestStateOperationResult TryMarkComplete(QuestWiringTestDependencies deps, string questId) =>
|
||||||
|
QuestStateOperations.TryMarkComplete(
|
||||||
|
PlayerId,
|
||||||
|
questId,
|
||||||
|
deps.QuestRegistry,
|
||||||
|
deps.ProgressStore,
|
||||||
|
deps.ItemRegistry,
|
||||||
|
deps.InventoryStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static void AcceptAndMarkComplete(QuestWiringTestDependencies deps, string questId)
|
private static void AcceptAndMarkComplete(QuestWiringTestDependencies deps, string questId)
|
||||||
{
|
{
|
||||||
Assert.True(TryAccept(deps, questId).Success);
|
Assert.True(TryAccept(deps, questId).Success);
|
||||||
Assert.True(QuestStateOperations.TryMarkComplete(
|
Assert.True(TryMarkComplete(deps, questId).Success);
|
||||||
PlayerId,
|
|
||||||
questId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
deps.TimeProvider).Success);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GatherResult TryGather(QuestWiringTestDependencies deps, string nodeId) =>
|
private static GatherResult TryGather(QuestWiringTestDependencies deps, string nodeId) =>
|
||||||
|
|
@ -259,6 +313,8 @@ public sealed class QuestObjectiveWiringTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static CraftResult TryCraft(QuestWiringTestDependencies deps, string recipeId) =>
|
private static CraftResult TryCraft(QuestWiringTestDependencies deps, string recipeId) =>
|
||||||
|
|
@ -275,6 +331,8 @@ public sealed class QuestObjectiveWiringTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static void SeedStack(QuestWiringTestDependencies deps, string itemId, int quantity)
|
private static void SeedStack(QuestWiringTestDependencies deps, string itemId, int quantity)
|
||||||
|
|
@ -308,6 +366,8 @@ public sealed class QuestObjectiveWiringTests
|
||||||
services.GetRequiredService<IEncounterProgressStore>(),
|
services.GetRequiredService<IEncounterProgressStore>(),
|
||||||
services.GetRequiredService<IEncounterCompletionStore>(),
|
services.GetRequiredService<IEncounterCompletionStore>(),
|
||||||
services.GetRequiredService<IEncounterCompleteEventStore>(),
|
services.GetRequiredService<IEncounterCompleteEventStore>(),
|
||||||
|
services.GetRequiredService<IPlayerPerkStateStore>(),
|
||||||
|
services.GetRequiredService<IRewardDeliveryStore>(),
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,5 +388,7 @@ public sealed class QuestObjectiveWiringTests
|
||||||
IEncounterProgressStore EncounterProgressStore,
|
IEncounterProgressStore EncounterProgressStore,
|
||||||
IEncounterCompletionStore CompletionStore,
|
IEncounterCompletionStore CompletionStore,
|
||||||
IEncounterCompleteEventStore CompleteEventStore,
|
IEncounterCompleteEventStore CompleteEventStore,
|
||||||
|
IPlayerPerkStateStore PerkStore,
|
||||||
|
IRewardDeliveryStore DeliveryStore,
|
||||||
TimeProvider TimeProvider);
|
TimeProvider TimeProvider);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using NeonSprawl.Server.Game.Gathering;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -230,6 +231,12 @@ public sealed class QuestProgressApiTests
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
Assert.True(result.Success);
|
Assert.True(result.Success);
|
||||||
}
|
}
|
||||||
|
|
@ -261,17 +268,34 @@ public sealed class QuestProgressApiTests
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.ItemRegistry,
|
deps.ItemRegistry,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
|
deps.TimeProvider);
|
||||||
|
|
||||||
|
private static QuestStateOperationResult TryMarkComplete(QuestWiringTestDependencies deps, string questId) =>
|
||||||
|
QuestStateOperations.TryMarkComplete(
|
||||||
|
PlayerId,
|
||||||
|
questId,
|
||||||
|
deps.QuestRegistry,
|
||||||
|
deps.ProgressStore,
|
||||||
|
deps.ItemRegistry,
|
||||||
|
deps.InventoryStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.XpStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static void AcceptAndMarkComplete(QuestWiringTestDependencies deps, string questId)
|
private static void AcceptAndMarkComplete(QuestWiringTestDependencies deps, string questId)
|
||||||
{
|
{
|
||||||
Assert.True(TryAccept(deps, questId).Success);
|
Assert.True(TryAccept(deps, questId).Success);
|
||||||
Assert.True(QuestStateOperations.TryMarkComplete(
|
Assert.True(TryMarkComplete(deps, questId).Success);
|
||||||
PlayerId,
|
|
||||||
questId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
deps.TimeProvider).Success);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GatherResult TryGather(QuestWiringTestDependencies deps, string nodeId) =>
|
private static GatherResult TryGather(QuestWiringTestDependencies deps, string nodeId) =>
|
||||||
|
|
@ -288,6 +312,8 @@ public sealed class QuestProgressApiTests
|
||||||
deps.InstanceStore,
|
deps.InstanceStore,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static CraftResult TryCraft(QuestWiringTestDependencies deps, string recipeId) =>
|
private static CraftResult TryCraft(QuestWiringTestDependencies deps, string recipeId) =>
|
||||||
|
|
@ -304,6 +330,8 @@ public sealed class QuestProgressApiTests
|
||||||
deps.PerkUnlockEngine,
|
deps.PerkUnlockEngine,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static void SeedStack(QuestWiringTestDependencies deps, string itemId, int quantity)
|
private static void SeedStack(QuestWiringTestDependencies deps, string itemId, int quantity)
|
||||||
|
|
@ -337,6 +365,8 @@ public sealed class QuestProgressApiTests
|
||||||
services.GetRequiredService<IEncounterProgressStore>(),
|
services.GetRequiredService<IEncounterProgressStore>(),
|
||||||
services.GetRequiredService<IEncounterCompletionStore>(),
|
services.GetRequiredService<IEncounterCompletionStore>(),
|
||||||
services.GetRequiredService<IEncounterCompleteEventStore>(),
|
services.GetRequiredService<IEncounterCompleteEventStore>(),
|
||||||
|
services.GetRequiredService<IPlayerPerkStateStore>(),
|
||||||
|
services.GetRequiredService<IRewardDeliveryStore>(),
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -357,5 +387,7 @@ public sealed class QuestProgressApiTests
|
||||||
IEncounterProgressStore EncounterProgressStore,
|
IEncounterProgressStore EncounterProgressStore,
|
||||||
IEncounterCompletionStore CompletionStore,
|
IEncounterCompletionStore CompletionStore,
|
||||||
IEncounterCompleteEventStore CompleteEventStore,
|
IEncounterCompleteEventStore CompleteEventStore,
|
||||||
|
IPlayerPerkStateStore PerkStore,
|
||||||
|
IRewardDeliveryStore DeliveryStore,
|
||||||
TimeProvider TimeProvider);
|
TimeProvider TimeProvider);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.PositionState;
|
using NeonSprawl.Server.Game.PositionState;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Tests;
|
using NeonSprawl.Server.Tests;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -59,12 +62,7 @@ public sealed class QuestStateOperationsTests
|
||||||
var deps = ResolveDependencies(factory);
|
var deps = ResolveDependencies(factory);
|
||||||
var timeProvider = TimeProvider.System;
|
var timeProvider = TimeProvider.System;
|
||||||
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
||||||
Assert.True(QuestStateOperations.TryMarkComplete(
|
Assert.True(TryMarkComplete(deps, GatherQuestId).Success);
|
||||||
PlayerId,
|
|
||||||
GatherQuestId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
timeProvider).Success);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = TryAccept(deps, RefineQuestId);
|
var result = TryAccept(deps, RefineQuestId);
|
||||||
|
|
@ -116,12 +114,7 @@ public sealed class QuestStateOperationsTests
|
||||||
var deps = ResolveDependencies(factory);
|
var deps = ResolveDependencies(factory);
|
||||||
var timeProvider = TimeProvider.System;
|
var timeProvider = TimeProvider.System;
|
||||||
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
||||||
Assert.True(QuestStateOperations.TryMarkComplete(
|
Assert.True(TryMarkComplete(deps, GatherQuestId).Success);
|
||||||
PlayerId,
|
|
||||||
GatherQuestId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
timeProvider).Success);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = TryAccept(deps, GatherQuestId);
|
var result = TryAccept(deps, GatherQuestId);
|
||||||
|
|
@ -230,12 +223,7 @@ public sealed class QuestStateOperationsTests
|
||||||
var deps = ResolveDependencies(factory);
|
var deps = ResolveDependencies(factory);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QuestStateOperations.TryMarkComplete(
|
var result = TryMarkComplete(deps, "prototype_quest_missing");
|
||||||
PlayerId,
|
|
||||||
"prototype_quest_missing",
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
TimeProvider.System);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.Success);
|
Assert.False(result.Success);
|
||||||
|
|
@ -265,12 +253,7 @@ public sealed class QuestStateOperationsTests
|
||||||
var deps = ResolveDependencies(factory);
|
var deps = ResolveDependencies(factory);
|
||||||
var timeProvider = TimeProvider.System;
|
var timeProvider = TimeProvider.System;
|
||||||
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
||||||
Assert.True(QuestStateOperations.TryMarkComplete(
|
Assert.True(TryMarkComplete(deps, GatherQuestId).Success);
|
||||||
PlayerId,
|
|
||||||
GatherQuestId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
timeProvider).Success);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = TryAdvanceStep(deps, GatherQuestId, 1);
|
var result = TryAdvanceStep(deps, GatherQuestId, 1);
|
||||||
|
|
@ -280,28 +263,83 @@ public sealed class QuestStateOperationsTests
|
||||||
Assert.Equal(QuestStateReasonCodes.AlreadyCompleted, result.ReasonCode);
|
Assert.Equal(QuestStateReasonCodes.AlreadyCompleted, result.ReasonCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryMarkComplete_ShouldDeliverGatherIntroBundle_WhenFirstCompletion()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
|
var deps = ResolveDependencies(factory);
|
||||||
|
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = TryMarkComplete(deps, GatherQuestId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(result.Success);
|
||||||
|
Assert.Null(result.ReasonCode);
|
||||||
|
Assert.Equal(QuestProgressStatus.Completed, result.Snapshot!.Status);
|
||||||
|
Assert.True(deps.DeliveryStore.TryGet(PlayerId, GatherQuestId, out var deliveryEvent));
|
||||||
|
Assert.Equal(25, deps.SkillProgressionStore.GetXpTotals(PlayerId).GetValueOrDefault("salvage"));
|
||||||
|
Assert.Single(deliveryEvent.GrantedSkillXp);
|
||||||
|
Assert.Equal("salvage", deliveryEvent.GrantedSkillXp[0].SkillId);
|
||||||
|
Assert.Equal(25, deliveryEvent.GrantedSkillXp[0].Amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryMarkComplete_ShouldNotDoubleGrant_WhenIdempotentReplay()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
|
var deps = ResolveDependencies(factory);
|
||||||
|
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
||||||
|
Assert.True(TryMarkComplete(deps, GatherQuestId).Success);
|
||||||
|
Assert.True(deps.DeliveryStore.TryGet(PlayerId, GatherQuestId, out var firstEvent));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var replay = TryMarkComplete(deps, GatherQuestId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(replay.Success);
|
||||||
|
Assert.Equal(25, deps.SkillProgressionStore.GetXpTotals(PlayerId).GetValueOrDefault("salvage"));
|
||||||
|
Assert.True(deps.DeliveryStore.TryGet(PlayerId, GatherQuestId, out var replayEvent));
|
||||||
|
Assert.Equal(firstEvent.IdempotencyKey, replayEvent.IdempotencyKey);
|
||||||
|
Assert.Equal(firstEvent.DeliveredAt, replayEvent.DeliveredAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryMarkComplete_ShouldDenyAndStayActive_WhenInventoryFull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
|
var deps = ResolveDependencies(factory);
|
||||||
|
AcceptAndComplete(deps, GatherQuestId);
|
||||||
|
AcceptAndComplete(deps, RefineQuestId);
|
||||||
|
AcceptAndComplete(deps, CombatQuestId);
|
||||||
|
Assert.True(TryAccept(deps, ChainQuestId).Success);
|
||||||
|
FillBag(deps);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = TryMarkComplete(deps, ChainQuestId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(result.Success);
|
||||||
|
Assert.Equal(RewardDeliveryReasonCodes.InventoryFull, result.ReasonCode);
|
||||||
|
Assert.True(deps.ProgressStore.TryGetProgress(PlayerId, ChainQuestId, out var progress));
|
||||||
|
Assert.Equal(QuestProgressStatus.Active, progress.Status);
|
||||||
|
Assert.False(deps.DeliveryStore.TryGet(PlayerId, ChainQuestId, out _));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TryMarkComplete_ShouldBeIdempotentAtOperationsLayer()
|
public async Task TryMarkComplete_ShouldBeIdempotentAtOperationsLayer()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
await using var factory = new InMemoryWebApplicationFactory();
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
var deps = ResolveDependencies(factory);
|
var deps = ResolveDependencies(factory);
|
||||||
var timeProvider = TimeProvider.System;
|
|
||||||
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
Assert.True(TryAccept(deps, GatherQuestId).Success);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var first = QuestStateOperations.TryMarkComplete(
|
var first = TryMarkComplete(deps, GatherQuestId);
|
||||||
PlayerId,
|
var second = TryMarkComplete(deps, GatherQuestId);
|
||||||
GatherQuestId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
timeProvider);
|
|
||||||
var second = QuestStateOperations.TryMarkComplete(
|
|
||||||
PlayerId,
|
|
||||||
GatherQuestId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
timeProvider);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(first.Success);
|
Assert.True(first.Success);
|
||||||
|
|
@ -319,15 +357,9 @@ public sealed class QuestStateOperationsTests
|
||||||
// Arrange
|
// Arrange
|
||||||
await using var factory = new InMemoryWebApplicationFactory();
|
await using var factory = new InMemoryWebApplicationFactory();
|
||||||
var deps = ResolveDependencies(factory);
|
var deps = ResolveDependencies(factory);
|
||||||
var timeProvider = TimeProvider.System;
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QuestStateOperations.TryMarkComplete(
|
var result = TryMarkComplete(deps, GatherQuestId);
|
||||||
PlayerId,
|
|
||||||
GatherQuestId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
timeProvider);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.Success);
|
Assert.False(result.Success);
|
||||||
|
|
@ -343,12 +375,7 @@ public sealed class QuestStateOperationsTests
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var accept = TryAccept(deps, GatherQuestId);
|
var accept = TryAccept(deps, GatherQuestId);
|
||||||
var complete = QuestStateOperations.TryMarkComplete(
|
var complete = TryMarkComplete(deps, GatherQuestId);
|
||||||
PlayerId,
|
|
||||||
GatherQuestId,
|
|
||||||
deps.QuestRegistry,
|
|
||||||
deps.ProgressStore,
|
|
||||||
TimeProvider.System);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(accept.Success);
|
Assert.True(accept.Success);
|
||||||
|
|
@ -380,16 +407,41 @@ public sealed class QuestStateOperationsTests
|
||||||
Assert.True(canWrite);
|
Assert.True(canWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void FillBag(QuestTestDependencies deps)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < PlayerInventorySnapshot.BagSlotCount; i++)
|
||||||
|
{
|
||||||
|
var add = PlayerInventoryOperations.TryAddStack(
|
||||||
|
PlayerId,
|
||||||
|
"survey_drone_kit",
|
||||||
|
quantity: 1,
|
||||||
|
deps.ItemRegistry,
|
||||||
|
deps.InventoryStore);
|
||||||
|
Assert.Equal(PlayerInventoryMutationKind.Applied, add.Kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void AcceptAndComplete(QuestTestDependencies deps, string questId)
|
private static void AcceptAndComplete(QuestTestDependencies deps, string questId)
|
||||||
{
|
{
|
||||||
Assert.True(TryAccept(deps, questId).Success);
|
Assert.True(TryAccept(deps, questId).Success);
|
||||||
Assert.True(QuestStateOperations.TryMarkComplete(
|
Assert.True(TryMarkComplete(deps, questId).Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static QuestStateOperationResult TryMarkComplete(QuestTestDependencies deps, string questId) =>
|
||||||
|
QuestStateOperations.TryMarkComplete(
|
||||||
PlayerId,
|
PlayerId,
|
||||||
questId,
|
questId,
|
||||||
deps.QuestRegistry,
|
deps.QuestRegistry,
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
deps.TimeProvider).Success);
|
deps.ItemRegistry,
|
||||||
}
|
deps.InventoryStore,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static QuestStateOperationResult TryAccept(QuestTestDependencies deps, string questId) =>
|
private static QuestStateOperationResult TryAccept(QuestTestDependencies deps, string questId) =>
|
||||||
TryAccept(deps, PlayerId, questId);
|
TryAccept(deps, PlayerId, questId);
|
||||||
|
|
@ -402,6 +454,12 @@ public sealed class QuestStateOperationsTests
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.ItemRegistry,
|
deps.ItemRegistry,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static QuestStateOperationResult TryAdvanceStep(QuestTestDependencies deps, string questId, int newStepIndex) =>
|
private static QuestStateOperationResult TryAdvanceStep(QuestTestDependencies deps, string questId, int newStepIndex) =>
|
||||||
|
|
@ -413,6 +471,12 @@ public sealed class QuestStateOperationsTests
|
||||||
deps.ProgressStore,
|
deps.ProgressStore,
|
||||||
deps.InventoryStore,
|
deps.InventoryStore,
|
||||||
deps.ItemRegistry,
|
deps.ItemRegistry,
|
||||||
|
deps.SkillRegistry,
|
||||||
|
deps.SkillProgressionStore,
|
||||||
|
deps.LevelCurve,
|
||||||
|
deps.PerkUnlockEngine,
|
||||||
|
deps.PerkStore,
|
||||||
|
deps.DeliveryStore,
|
||||||
deps.TimeProvider);
|
deps.TimeProvider);
|
||||||
|
|
||||||
private static QuestTestDependencies ResolveDependencies(InMemoryWebApplicationFactory factory)
|
private static QuestTestDependencies ResolveDependencies(InMemoryWebApplicationFactory factory)
|
||||||
|
|
@ -423,6 +487,12 @@ public sealed class QuestStateOperationsTests
|
||||||
services.GetRequiredService<IPlayerQuestStateStore>(),
|
services.GetRequiredService<IPlayerQuestStateStore>(),
|
||||||
services.GetRequiredService<IPlayerInventoryStore>(),
|
services.GetRequiredService<IPlayerInventoryStore>(),
|
||||||
services.GetRequiredService<IItemDefinitionRegistry>(),
|
services.GetRequiredService<IItemDefinitionRegistry>(),
|
||||||
|
services.GetRequiredService<ISkillDefinitionRegistry>(),
|
||||||
|
services.GetRequiredService<IPlayerSkillProgressionStore>(),
|
||||||
|
services.GetRequiredService<ISkillLevelCurve>(),
|
||||||
|
services.GetRequiredService<PerkUnlockEngine>(),
|
||||||
|
services.GetRequiredService<IPlayerPerkStateStore>(),
|
||||||
|
services.GetRequiredService<IRewardDeliveryStore>(),
|
||||||
TimeProvider.System);
|
TimeProvider.System);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,5 +501,11 @@ public sealed class QuestStateOperationsTests
|
||||||
IPlayerQuestStateStore ProgressStore,
|
IPlayerQuestStateStore ProgressStore,
|
||||||
IPlayerInventoryStore InventoryStore,
|
IPlayerInventoryStore InventoryStore,
|
||||||
IItemDefinitionRegistry ItemRegistry,
|
IItemDefinitionRegistry ItemRegistry,
|
||||||
|
ISkillDefinitionRegistry SkillRegistry,
|
||||||
|
IPlayerSkillProgressionStore SkillProgressionStore,
|
||||||
|
ISkillLevelCurve LevelCurve,
|
||||||
|
PerkUnlockEngine PerkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore PerkStore,
|
||||||
|
IRewardDeliveryStore DeliveryStore,
|
||||||
TimeProvider TimeProvider);
|
TimeProvider TimeProvider);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,12 @@ using NeonSprawl.Server.Game.Combat;
|
||||||
using NeonSprawl.Server.Game.Encounters;
|
using NeonSprawl.Server.Game.Encounters;
|
||||||
using NeonSprawl.Server.Game.Gigs;
|
using NeonSprawl.Server.Game.Gigs;
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.PositionState;
|
using NeonSprawl.Server.Game.PositionState;
|
||||||
using NeonSprawl.Server.Game.Npc;
|
using NeonSprawl.Server.Game.Npc;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Game.Targeting;
|
using NeonSprawl.Server.Game.Targeting;
|
||||||
using NeonSprawl.Server.Game.World;
|
using NeonSprawl.Server.Game.World;
|
||||||
|
|
||||||
|
|
@ -81,6 +84,12 @@ public static class AbilityCastApi
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IQuestDefinitionRegistry questRegistry,
|
IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore questProgressStore,
|
IPlayerQuestStateStore questProgressStore,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
TimeProvider clock) =>
|
TimeProvider clock) =>
|
||||||
{
|
{
|
||||||
|
|
@ -282,6 +291,12 @@ public static class AbilityCastApi
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
questRegistry,
|
questRegistry,
|
||||||
questProgressStore,
|
questProgressStore,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
clock,
|
clock,
|
||||||
loggerFactory.CreateLogger(nameof(EncounterCombatWiring)));
|
loggerFactory.CreateLogger(nameof(EncounterCombatWiring)));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Crafting;
|
namespace NeonSprawl.Server.Game.Crafting;
|
||||||
|
|
@ -30,6 +31,8 @@ public static class CraftOperations
|
||||||
PerkUnlockEngine perkUnlockEngine,
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
IQuestDefinitionRegistry questRegistry,
|
IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
if (quantity <= 0)
|
if (quantity <= 0)
|
||||||
|
|
@ -165,6 +168,12 @@ public static class CraftOperations
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
xpStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
return new CraftResult(
|
return new CraftResult(
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.PositionState;
|
using NeonSprawl.Server.Game.PositionState;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Crafting;
|
namespace NeonSprawl.Server.Game.Crafting;
|
||||||
|
|
@ -18,7 +19,8 @@ public static class PlayerCraftApi
|
||||||
IPlayerInventoryStore inventoryStore, ISkillDefinitionRegistry skillRegistry,
|
IPlayerInventoryStore inventoryStore, ISkillDefinitionRegistry skillRegistry,
|
||||||
IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve,
|
IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve,
|
||||||
PerkUnlockEngine perkUnlockEngine, IQuestDefinitionRegistry questRegistry,
|
PerkUnlockEngine perkUnlockEngine, IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore progressStore, TimeProvider timeProvider) =>
|
IPlayerQuestStateStore progressStore, IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore, TimeProvider timeProvider) =>
|
||||||
{
|
{
|
||||||
if (body is null || body.SchemaVersion != PlayerCraftRequest.CurrentSchemaVersion)
|
if (body is null || body.SchemaVersion != PlayerCraftRequest.CurrentSchemaVersion)
|
||||||
{
|
{
|
||||||
|
|
@ -50,6 +52,8 @@ public static class PlayerCraftApi
|
||||||
perkUnlockEngine,
|
perkUnlockEngine,
|
||||||
questRegistry,
|
questRegistry,
|
||||||
progressStore,
|
progressStore,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
if (!result.Success &&
|
if (!result.Success &&
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Encounters;
|
namespace NeonSprawl.Server.Game.Encounters;
|
||||||
|
|
||||||
|
|
@ -26,6 +29,12 @@ public static class EncounterCombatWiring
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IQuestDefinitionRegistry questRegistry,
|
IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore questProgressStore,
|
IPlayerQuestStateStore questProgressStore,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider,
|
TimeProvider timeProvider,
|
||||||
ILogger? logger = null)
|
ILogger? logger = null)
|
||||||
{
|
{
|
||||||
|
|
@ -84,6 +93,12 @@ public static class EncounterCombatWiring
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
questRegistry,
|
questRegistry,
|
||||||
questProgressStore,
|
questProgressStore,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
if (!completion.Success)
|
if (!completion.Success)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Encounters;
|
namespace NeonSprawl.Server.Game.Encounters;
|
||||||
|
|
||||||
|
|
@ -29,6 +32,12 @@ public static class EncounterCompletionOperations
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IQuestDefinitionRegistry questRegistry,
|
IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore questProgressStore,
|
IPlayerQuestStateStore questProgressStore,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
if (!encounterRegistry.TryNormalizeKnown(encounterId, out var normalizedEncounterId) ||
|
if (!encounterRegistry.TryNormalizeKnown(encounterId, out var normalizedEncounterId) ||
|
||||||
|
|
@ -145,6 +154,12 @@ public static class EncounterCompletionOperations
|
||||||
questProgressStore,
|
questProgressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
return new EncounterCompletionResult(
|
return new EncounterCompletionResult(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Gathering;
|
namespace NeonSprawl.Server.Game.Gathering;
|
||||||
|
|
@ -31,6 +32,8 @@ public static class GatherOperations
|
||||||
IResourceNodeInstanceStore instanceStore,
|
IResourceNodeInstanceStore instanceStore,
|
||||||
IQuestDefinitionRegistry questRegistry,
|
IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
var key = ResourceNodeInstanceIds.Normalize(interactableId);
|
var key = ResourceNodeInstanceIds.Normalize(interactableId);
|
||||||
|
|
@ -140,6 +143,12 @@ public static class GatherOperations
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
xpStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
return new GatherResult(
|
return new GatherResult(
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using NeonSprawl.Server.Game.Items;
|
||||||
using NeonSprawl.Server.Game.Mastery;
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.PositionState;
|
using NeonSprawl.Server.Game.PositionState;
|
||||||
using NeonSprawl.Server.Game.Quests;
|
using NeonSprawl.Server.Game.Quests;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
using NeonSprawl.Server.Game.Skills;
|
using NeonSprawl.Server.Game.Skills;
|
||||||
using NeonSprawl.Server.Game.World;
|
using NeonSprawl.Server.Game.World;
|
||||||
|
|
||||||
|
|
@ -23,7 +24,8 @@ public static class InteractionApi
|
||||||
IPlayerInventoryStore inventoryStore, ISkillDefinitionRegistry skillRegistry,
|
IPlayerInventoryStore inventoryStore, ISkillDefinitionRegistry skillRegistry,
|
||||||
IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve, PerkUnlockEngine perkUnlockEngine,
|
IPlayerSkillProgressionStore xpStore, ISkillLevelCurve levelCurve, PerkUnlockEngine perkUnlockEngine,
|
||||||
IResourceNodeInstanceStore nodeInstanceStore, IQuestDefinitionRegistry questRegistry,
|
IResourceNodeInstanceStore nodeInstanceStore, IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore progressStore, TimeProvider timeProvider) =>
|
IPlayerQuestStateStore progressStore, IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore, TimeProvider timeProvider) =>
|
||||||
{
|
{
|
||||||
if (body is null || body.SchemaVersion != InteractionRequest.CurrentSchemaVersion)
|
if (body is null || body.SchemaVersion != InteractionRequest.CurrentSchemaVersion)
|
||||||
{
|
{
|
||||||
|
|
@ -91,6 +93,8 @@ public static class InteractionApi
|
||||||
nodeInstanceStore,
|
nodeInstanceStore,
|
||||||
questRegistry,
|
questRegistry,
|
||||||
progressStore,
|
progressStore,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
if (!gather.Success)
|
if (!gather.Success)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.PositionState;
|
using NeonSprawl.Server.Game.PositionState;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Quests;
|
namespace NeonSprawl.Server.Game.Quests;
|
||||||
|
|
||||||
|
|
@ -13,7 +16,9 @@ public static class QuestAcceptApi
|
||||||
(string id, string questId, QuestAcceptRequest? body, IPositionStateStore positions,
|
(string id, string questId, QuestAcceptRequest? body, IPositionStateStore positions,
|
||||||
IQuestDefinitionRegistry questRegistry, IPlayerQuestStateStore progressStore,
|
IQuestDefinitionRegistry questRegistry, IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore, IItemDefinitionRegistry itemRegistry,
|
IPlayerInventoryStore inventoryStore, IItemDefinitionRegistry itemRegistry,
|
||||||
TimeProvider timeProvider) =>
|
ISkillDefinitionRegistry skillRegistry, IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve, PerkUnlockEngine perkUnlockEngine, IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore, TimeProvider timeProvider) =>
|
||||||
{
|
{
|
||||||
if (body is not null &&
|
if (body is not null &&
|
||||||
body.SchemaVersion != 0 &&
|
body.SchemaVersion != 0 &&
|
||||||
|
|
@ -35,6 +40,12 @@ public static class QuestAcceptApi
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
return Results.Json(MapResponse(result));
|
return Results.Json(MapResponse(result));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Quests;
|
namespace NeonSprawl.Server.Game.Quests;
|
||||||
|
|
||||||
|
|
@ -19,6 +22,12 @@ public static class QuestObjectiveWiring
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider,
|
TimeProvider timeProvider,
|
||||||
ILogger? logger = null)
|
ILogger? logger = null)
|
||||||
{
|
{
|
||||||
|
|
@ -59,6 +68,12 @@ public static class QuestObjectiveWiring
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -76,6 +91,12 @@ public static class QuestObjectiveWiring
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider,
|
TimeProvider timeProvider,
|
||||||
ILogger? logger = null)
|
ILogger? logger = null)
|
||||||
{
|
{
|
||||||
|
|
@ -116,6 +137,12 @@ public static class QuestObjectiveWiring
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -132,6 +159,12 @@ public static class QuestObjectiveWiring
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider,
|
TimeProvider timeProvider,
|
||||||
ILogger? logger = null)
|
ILogger? logger = null)
|
||||||
{
|
{
|
||||||
|
|
@ -171,6 +204,12 @@ public static class QuestObjectiveWiring
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -209,6 +248,12 @@ public static class QuestObjectiveWiring
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider,
|
TimeProvider timeProvider,
|
||||||
ILogger? logger = null)
|
ILogger? logger = null)
|
||||||
{
|
{
|
||||||
|
|
@ -227,6 +272,12 @@ public static class QuestObjectiveWiring
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
@ -286,6 +337,12 @@ public static class QuestObjectiveWiring
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
for (var iteration = 0; iteration < MaxStepCompletionIterations; iteration++)
|
for (var iteration = 0; iteration < MaxStepCompletionIterations; iteration++)
|
||||||
|
|
@ -316,6 +373,14 @@ public static class QuestObjectiveWiring
|
||||||
questId,
|
questId,
|
||||||
questRegistry,
|
questRegistry,
|
||||||
progressStore,
|
progressStore,
|
||||||
|
itemRegistry,
|
||||||
|
inventoryStore,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +393,12 @@ public static class QuestObjectiveWiring
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
if (!advance.Success)
|
if (!advance.Success)
|
||||||
|
|
@ -343,6 +414,12 @@ public static class QuestObjectiveWiring
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
foreach (var definition in questRegistry.GetDefinitionsInIdOrder())
|
foreach (var definition in questRegistry.GetDefinitionsInIdOrder())
|
||||||
|
|
@ -356,6 +433,12 @@ public static class QuestObjectiveWiring
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
using NeonSprawl.Server.Game.PositionState;
|
using NeonSprawl.Server.Game.PositionState;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Quests;
|
namespace NeonSprawl.Server.Game.Quests;
|
||||||
|
|
||||||
|
|
@ -16,7 +19,10 @@ public static class QuestProgressApi
|
||||||
"/game/players/{id}/quest-progress",
|
"/game/players/{id}/quest-progress",
|
||||||
(string id, IPositionStateStore positions, IQuestDefinitionRegistry questRegistry,
|
(string id, IPositionStateStore positions, IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore progressStore, IPlayerInventoryStore inventoryStore,
|
IPlayerQuestStateStore progressStore, IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry, TimeProvider timeProvider, ILogger<Program>? logger) =>
|
IItemDefinitionRegistry itemRegistry, ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore, ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine, IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore, TimeProvider timeProvider, ILogger<Program>? logger) =>
|
||||||
{
|
{
|
||||||
var trimmedId = id.Trim();
|
var trimmedId = id.Trim();
|
||||||
if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
|
if (trimmedId.Length == 0 || !positions.TryGetPosition(trimmedId, out _))
|
||||||
|
|
@ -30,6 +36,12 @@ public static class QuestProgressApi
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider,
|
timeProvider,
|
||||||
logger);
|
logger);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
using NeonSprawl.Server.Game.Items;
|
using NeonSprawl.Server.Game.Items;
|
||||||
|
using NeonSprawl.Server.Game.Mastery;
|
||||||
|
using NeonSprawl.Server.Game.Rewards;
|
||||||
|
using NeonSprawl.Server.Game.Skills;
|
||||||
|
|
||||||
namespace NeonSprawl.Server.Game.Quests;
|
namespace NeonSprawl.Server.Game.Quests;
|
||||||
|
|
||||||
|
|
@ -20,6 +23,12 @@ public static class QuestStateOperations
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
if (!questRegistry.TryNormalizeKnown(questId, out var normalizedQuestId) ||
|
if (!questRegistry.TryNormalizeKnown(questId, out var normalizedQuestId) ||
|
||||||
|
|
@ -73,6 +82,12 @@ public static class QuestStateOperations
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
if (progressStore.TryGetProgress(playerId, normalizedQuestId, out var refreshed))
|
if (progressStore.TryGetProgress(playerId, normalizedQuestId, out var refreshed))
|
||||||
|
|
@ -97,6 +112,12 @@ public static class QuestStateOperations
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
IPlayerInventoryStore inventoryStore,
|
IPlayerInventoryStore inventoryStore,
|
||||||
IItemDefinitionRegistry itemRegistry,
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
if (!questRegistry.TryNormalizeKnown(questId, out var normalizedQuestId) ||
|
if (!questRegistry.TryNormalizeKnown(questId, out var normalizedQuestId) ||
|
||||||
|
|
@ -146,6 +167,12 @@ public static class QuestStateOperations
|
||||||
progressStore,
|
progressStore,
|
||||||
inventoryStore,
|
inventoryStore,
|
||||||
itemRegistry,
|
itemRegistry,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
timeProvider);
|
timeProvider);
|
||||||
|
|
||||||
if (progressStore.TryGetProgress(playerId, normalizedQuestId, out var refreshed))
|
if (progressStore.TryGetProgress(playerId, normalizedQuestId, out var refreshed))
|
||||||
|
|
@ -160,17 +187,26 @@ public static class QuestStateOperations
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Marks a quest complete. Idempotent: replays return success with the existing snapshot.
|
/// Delivers <see cref="QuestDefRow.CompletionRewardBundle"/> then marks complete (NEO-128).
|
||||||
|
/// Idempotent: replays return success with the existing snapshot without re-delivering.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static QuestStateOperationResult TryMarkComplete(
|
public static QuestStateOperationResult TryMarkComplete(
|
||||||
string playerId,
|
string playerId,
|
||||||
string questId,
|
string questId,
|
||||||
IQuestDefinitionRegistry questRegistry,
|
IQuestDefinitionRegistry questRegistry,
|
||||||
IPlayerQuestStateStore progressStore,
|
IPlayerQuestStateStore progressStore,
|
||||||
|
IItemDefinitionRegistry itemRegistry,
|
||||||
|
IPlayerInventoryStore inventoryStore,
|
||||||
|
ISkillDefinitionRegistry skillRegistry,
|
||||||
|
IPlayerSkillProgressionStore skillProgressionStore,
|
||||||
|
ISkillLevelCurve levelCurve,
|
||||||
|
PerkUnlockEngine perkUnlockEngine,
|
||||||
|
IPlayerPerkStateStore perkStore,
|
||||||
|
IRewardDeliveryStore deliveryStore,
|
||||||
TimeProvider timeProvider)
|
TimeProvider timeProvider)
|
||||||
{
|
{
|
||||||
if (!questRegistry.TryNormalizeKnown(questId, out var normalizedQuestId) ||
|
if (!questRegistry.TryNormalizeKnown(questId, out var normalizedQuestId) ||
|
||||||
!questRegistry.TryGetDefinition(normalizedQuestId, out _))
|
!questRegistry.TryGetDefinition(normalizedQuestId, out var definition))
|
||||||
{
|
{
|
||||||
return Deny(QuestStateReasonCodes.UnknownQuest);
|
return Deny(QuestStateReasonCodes.UnknownQuest);
|
||||||
}
|
}
|
||||||
|
|
@ -190,6 +226,25 @@ public static class QuestStateOperations
|
||||||
return Deny(QuestStateReasonCodes.NotActive);
|
return Deny(QuestStateReasonCodes.NotActive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var delivery = RewardRouterOperations.TryDeliverQuestCompletion(
|
||||||
|
playerId,
|
||||||
|
normalizedQuestId,
|
||||||
|
definition.CompletionRewardBundle,
|
||||||
|
itemRegistry,
|
||||||
|
inventoryStore,
|
||||||
|
skillRegistry,
|
||||||
|
skillProgressionStore,
|
||||||
|
levelCurve,
|
||||||
|
perkUnlockEngine,
|
||||||
|
perkStore,
|
||||||
|
deliveryStore,
|
||||||
|
timeProvider);
|
||||||
|
|
||||||
|
if (!delivery.Success)
|
||||||
|
{
|
||||||
|
return Deny(delivery.ReasonCode ?? RewardDeliveryReasonCodes.InvalidQuestId, progress);
|
||||||
|
}
|
||||||
|
|
||||||
if (progressStore.TryMarkComplete(playerId, normalizedQuestId, timeProvider.GetUtcNow(), out var snapshot))
|
if (progressStore.TryMarkComplete(playerId, normalizedQuestId, timeProvider.GetUtcNow(), out var snapshot))
|
||||||
{
|
{
|
||||||
// --- Telemetry hook site (NEO-121): future E9.M1 catalog event `quest_complete` ---
|
// --- Telemetry hook site (NEO-121): future E9.M1 catalog event `quest_complete` ---
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ Completed rows cannot regress to active without a reset API (none in prototype).
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| **`TryAccept`** | Validates quest id + **`prerequisiteQuestIds`** (each prerequisite must be **completed**), then activates at step 0. |
|
| **`TryAccept`** | Validates quest id + **`prerequisiteQuestIds`** (each prerequisite must be **completed**), then activates at step 0. |
|
||||||
| **`TryAdvanceStep`** | Requires active row; **`newStepIndex`** must be greater than current and less than step count from **`QuestDefRow`**. |
|
| **`TryAdvanceStep`** | Requires active row; **`newStepIndex`** must be greater than current and less than step count from **`QuestDefRow`**. |
|
||||||
| **`TryMarkComplete`** | Marks active quest complete. **Idempotent:** second call returns **`success: true`** with unchanged snapshot (unlike encounter completion). |
|
| **`TryMarkComplete`** | **NEO-128:** delivers **`completionRewardBundle`** via **`RewardRouterOperations`** (deliver-then-mark), then marks active quest complete. **Idempotent:** when already **completed**, returns **`success: true`** without calling the router; replay after first-time complete also skips re-delivery. On delivery deny, quest stays **active** and **`reasonCode`** passthroughs router codes (e.g. **`inventory_full`**). |
|
||||||
|
|
||||||
**Reason codes:**
|
**Reason codes:**
|
||||||
|
|
||||||
|
|
@ -297,7 +297,7 @@ Encounter loot remains on **`IEncounterCompleteEventStore`** (E5.M3); quest comp
|
||||||
| **`unknown_skill`**, **`invalid_amount`**, **`source_kind_not_allowed`** | Skill XP grant denied. |
|
| **`unknown_skill`**, **`invalid_amount`**, **`source_kind_not_allowed`** | Skill XP grant denied. |
|
||||||
| **`progression_store_missing`** | Skill progression store could not apply XP delta. |
|
| **`progression_store_missing`** | Skill progression store could not apply XP delta. |
|
||||||
|
|
||||||
Apply order: **items first**, then **skill XP**. Inventory deny or skill deny fails closed without store record (compensating item rollback on partial apply or skill failure after items). Idempotent replay checks **`TryGet`** before apply. If grants succeed but **`TryRecord`** returns `false` (concurrent race), this call **rolls back** its item grants and calls **`SkillProgressionGrantOperations.TryRevertAppliedSkillGrants`** — negative XP delta per applied skill row plus removal of perks unlocked during this call only — then **`ReevaluateAfterLevelUp`** at post-rollback levels so a concurrent winner's path-auto unlocks are preserved — then returns the existing store event (mirror **`EncounterCompletionOperations`** compensating rollback when **`TryMarkCompleted`** loses). Quest-state wiring lands in NEO-128. Plan: [NEO-127 implementation plan](../../docs/plans/NEO-127-implementation-plan.md).
|
Apply order: **items first**, then **skill XP**. Inventory deny or skill deny fails closed without store record (compensating item rollback on partial apply or skill failure after items). Idempotent replay checks **`TryGet`** before apply. If grants succeed but **`TryRecord`** returns `false` (concurrent race), this call **rolls back** its item grants and calls **`SkillProgressionGrantOperations.TryRevertAppliedSkillGrants`** — negative XP delta per applied skill row plus removal of perks unlocked during this call only — then **`ReevaluateAfterLevelUp`** at post-rollback levels so a concurrent winner's path-auto unlocks are preserved — then returns the existing store event (mirror **`EncounterCompletionOperations`** compensating rollback when **`TryMarkCompleted`** loses). **Quest-state wiring (NEO-128):** **`QuestStateOperations.TryMarkComplete`** and **`QuestObjectiveWiring`** terminal-step completion call this operation before **`IPlayerQuestStateStore.TryMarkComplete`**. Plans: [NEO-127](../../docs/plans/NEO-127-implementation-plan.md), [NEO-128](../../docs/plans/NEO-128-implementation-plan.md).
|
||||||
|
|
||||||
## Encounter definitions (NEO-103)
|
## Encounter definitions (NEO-103)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue