NEO-108: Fix Bruno encounter-progress and combat-health smokes.
Use collection-relative ./scripts require path; reset encounter stores in combat-targets dev fixture for inactive GET; combat-health uses slot 3.pull/147/head
parent
25f3714554
commit
a5182a4c14
|
|
@ -29,11 +29,13 @@ script:pre-request {
|
|||
jsonHeaders,
|
||||
);
|
||||
|
||||
const slotIndex = 3;
|
||||
|
||||
await axios.post(
|
||||
`${baseUrl}/game/players/${playerId}/hotbar-loadout`,
|
||||
{
|
||||
schemaVersion: 1,
|
||||
slots: [{ slotIndex: 0, abilityId: "prototype_pulse" }],
|
||||
slots: [{ slotIndex, abilityId: "prototype_pulse" }],
|
||||
},
|
||||
jsonHeaders,
|
||||
);
|
||||
|
|
@ -48,7 +50,7 @@ script:pre-request {
|
|||
`${baseUrl}/game/players/${playerId}/ability-cast`,
|
||||
{
|
||||
schemaVersion: 1,
|
||||
slotIndex: 0,
|
||||
slotIndex,
|
||||
abilityId: "prototype_pulse",
|
||||
targetId: "prototype_npc_elite",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ docs {
|
|||
|
||||
script:pre-request {
|
||||
const axios = require("axios");
|
||||
const { resetPrototypeCombatTargets } = require("../scripts/combat-targets-reset-helper.js");
|
||||
const { resetPrototypeCombatTargets } = require("./scripts/combat-targets-reset-helper.js");
|
||||
const baseUrl = bru.getEnvVar("baseUrl") || bru.getVar("baseUrl");
|
||||
const playerId = bru.getEnvVar("playerId") || bru.getVar("playerId");
|
||||
const jsonHeaders = { headers: { "Content-Type": "application/json" } };
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@ meta {
|
|||
|
||||
docs {
|
||||
NEO-108: fresh player row before engagement — inactive with empty defeatedTargetIds.
|
||||
Pre-request uses combat-targets dev fixture (also clears encounter progress for dev-local-1).
|
||||
}
|
||||
|
||||
script:pre-request {
|
||||
const { resetPrototypeCombatTargets } = require("./scripts/combat-targets-reset-helper.js");
|
||||
await resetPrototypeCombatTargets(bru);
|
||||
}
|
||||
|
||||
get {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Net.Http.Json;
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NeonSprawl.Server.Game.Combat;
|
||||
using NeonSprawl.Server.Game.Encounters;
|
||||
using NeonSprawl.Server.Game.Npc;
|
||||
using NeonSprawl.Server.Tests;
|
||||
using Xunit;
|
||||
|
|
@ -23,6 +24,20 @@ public sealed class CombatTargetFixtureApiTests
|
|||
var threatStore = factory.Services.GetRequiredService<IThreatStateStore>();
|
||||
var runtimeStore = factory.Services.GetRequiredService<INpcRuntimeStateStore>();
|
||||
var playerHealthStore = factory.Services.GetRequiredService<IPlayerCombatHealthStore>();
|
||||
var progressStore = factory.Services.GetRequiredService<IEncounterProgressStore>();
|
||||
var completionStore = factory.Services.GetRequiredService<IEncounterCompletionStore>();
|
||||
var eventStore = factory.Services.GetRequiredService<IEncounterCompleteEventStore>();
|
||||
_ = progressStore.TryActivate("dev-local-1", "prototype_combat_pocket");
|
||||
_ = progressStore.TryAddDefeatedTarget("dev-local-1", "prototype_combat_pocket", "prototype_npc_melee");
|
||||
_ = completionStore.TryMarkCompleted("dev-local-1", "prototype_combat_pocket", DateTimeOffset.UtcNow);
|
||||
_ = eventStore.TryRecord(
|
||||
new EncounterCompleteEvent(
|
||||
"dev-local-1",
|
||||
"prototype_combat_pocket",
|
||||
"prototype_combat_pocket_clear",
|
||||
[new EncounterGrantApplied("scrap_metal_bulk", 10)],
|
||||
DateTimeOffset.UtcNow,
|
||||
"dev-local-1:prototype_combat_pocket"));
|
||||
_ = store.TryApplyDamage(PrototypeNpcRegistry.PrototypeNpcMeleeId, 100, out _);
|
||||
_ = playerHealthStore.TryApplyDamage("dev-local-1", 40, out _);
|
||||
_ = threatStore.TrySetHolder(PrototypeNpcRegistry.PrototypeNpcMeleeId, "dev-local-1");
|
||||
|
|
@ -59,6 +74,9 @@ public sealed class CombatTargetFixtureApiTests
|
|||
playerHealthStore.TryGet("dev-local-1", out var playerHealth);
|
||||
Assert.Equal(PlayerCombatHealthDefaults.MaxHp, playerHealth.CurrentHp);
|
||||
Assert.False(playerHealth.Defeated);
|
||||
Assert.False(progressStore.TryGetProgress("dev-local-1", "prototype_combat_pocket", out _));
|
||||
Assert.False(completionStore.IsCompleted("dev-local-1", "prototype_combat_pocket"));
|
||||
Assert.False(eventStore.TryGet("dev-local-1", "prototype_combat_pocket", out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.Extensions.Options;
|
||||
using NeonSprawl.Server.Game.Encounters;
|
||||
using NeonSprawl.Server.Game.Npc;
|
||||
using NeonSprawl.Server.Game.PositionState;
|
||||
|
||||
|
|
@ -17,6 +18,10 @@ public static class CombatTargetFixtureApi
|
|||
IThreatStateStore threatStore,
|
||||
INpcRuntimeStateStore runtimeStore,
|
||||
IPlayerCombatHealthStore playerHealthStore,
|
||||
IEncounterDefinitionRegistry encounterRegistry,
|
||||
IEncounterProgressStore encounterProgressStore,
|
||||
IEncounterCompletionStore encounterCompletionStore,
|
||||
IEncounterCompleteEventStore encounterCompleteEventStore,
|
||||
IOptions<GamePositionOptions> gameOptions) =>
|
||||
{
|
||||
if (body is null || body.SchemaVersion != CombatTargetFixtureRequest.CurrentSchemaVersion)
|
||||
|
|
@ -41,6 +46,13 @@ public static class CombatTargetFixtureApi
|
|||
return Results.NotFound();
|
||||
}
|
||||
|
||||
EncounterDevFixtureOperations.ClearPlayerEncounterState(
|
||||
devPlayerId,
|
||||
encounterRegistry,
|
||||
encounterProgressStore,
|
||||
encounterCompletionStore,
|
||||
encounterCompleteEventStore);
|
||||
|
||||
return Results.Json(
|
||||
new CombatTargetFixtureResponse
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
namespace NeonSprawl.Server.Game.Encounters;
|
||||
|
||||
/// <summary>Clears per-player encounter stores for dev Bruno/QA resets (NEO-108).</summary>
|
||||
internal static class EncounterDevFixtureOperations
|
||||
{
|
||||
/// <summary>Removes progress, completion, and event rows for <paramref name="playerId"/> across catalog encounters.</summary>
|
||||
public static void ClearPlayerEncounterState(
|
||||
string playerId,
|
||||
IEncounterDefinitionRegistry encounterRegistry,
|
||||
IEncounterProgressStore progressStore,
|
||||
IEncounterCompletionStore completionStore,
|
||||
IEncounterCompleteEventStore eventStore)
|
||||
{
|
||||
foreach (var def in encounterRegistry.GetDefinitionsInIdOrder())
|
||||
{
|
||||
if (progressStore is InMemoryEncounterProgressStore inMemoryProgress)
|
||||
{
|
||||
inMemoryProgress.TryClear(playerId, def.Id);
|
||||
}
|
||||
|
||||
if (completionStore is InMemoryEncounterCompletionStore inMemoryCompletion)
|
||||
{
|
||||
inMemoryCompletion.TryClear(playerId, def.Id);
|
||||
}
|
||||
|
||||
if (eventStore is InMemoryEncounterCompleteEventStore inMemoryEvent)
|
||||
{
|
||||
inMemoryEvent.TryClear(playerId, def.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,4 +48,21 @@ public sealed class InMemoryEncounterCompleteEventStore : IEncounterCompleteEven
|
|||
return eventsByKey.TryGetValue(key, out completeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Dev fixture only — removes one player+encounter event row (NEO-108 Bruno reset).</summary>
|
||||
public bool TryClear(string playerId, string encounterId)
|
||||
{
|
||||
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
|
||||
if (key.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (keyLocks.GetOrAdd(key, _ => new object()))
|
||||
{
|
||||
var removed = eventsByKey.TryRemove(key, out _);
|
||||
keyLocks.TryRemove(key, out _);
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,4 +60,21 @@ public sealed class InMemoryEncounterCompletionStore : IEncounterCompletionStore
|
|||
return completedAtByKey.TryGetValue(key, out completedAt);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Dev fixture only — removes one player+encounter completion row (NEO-108 Bruno reset).</summary>
|
||||
public bool TryClear(string playerId, string encounterId)
|
||||
{
|
||||
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
|
||||
if (key.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (keyLocks.GetOrAdd(key, _ => new object()))
|
||||
{
|
||||
var removed = completedAtByKey.TryRemove(key, out _);
|
||||
keyLocks.TryRemove(key, out _);
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,4 +85,21 @@ public sealed class InMemoryEncounterProgressStore : IEncounterProgressStore
|
|||
return row.DefeatedNpcInstanceIds.Add(npcKey);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Dev fixture only — removes one player+encounter progress row (NEO-108 Bruno reset).</summary>
|
||||
public bool TryClear(string playerId, string encounterId)
|
||||
{
|
||||
var key = EncounterProgressIds.MakeProgressKey(playerId, encounterId);
|
||||
if (key.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (keyLocks.GetOrAdd(key, _ => new object()))
|
||||
{
|
||||
var removed = byKey.TryRemove(key, out _);
|
||||
keyLocks.TryRemove(key, out _);
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ Plan: [NEO-80 implementation plan](../../docs/plans/NEO-80-implementation-plan.m
|
|||
curl -sS -i "http://localhost:5253/game/world/combat-targets"
|
||||
```
|
||||
|
||||
**Dev combat-target fixture (Bruno/manual QA):** When `Game:EnableCombatTargetFixtureApi` is **true** (default in **Development** via `appsettings.Development.json`, **Testing**, or CI Bruno step) or the host environment is **Development** / **Testing**, **`POST /game/__dev/combat-targets-fixture`** with `schemaVersion` **1** resets all **`PrototypeNpcRegistry`** instances to catalog full HP via **`TryResetToFull`**, clears all prototype aggro holders (**NEO-92**), resets NPC runtime behavior rows to **`idle`** (**NEO-93**), and restores dev player combat HP to full (**NEO-95**). **404** when disabled. Bruno: `scripts/combat-targets-reset-helper.js`.
|
||||
**Dev combat-target fixture (Bruno/manual QA):** When `Game:EnableCombatTargetFixtureApi` is **true** (default in **Development** via `appsettings.Development.json`, **Testing**, or CI Bruno step) or the host environment is **Development** / **Testing**, **`POST /game/__dev/combat-targets-fixture`** with `schemaVersion` **1** resets all **`PrototypeNpcRegistry`** instances to catalog full HP via **`TryResetToFull`**, clears all prototype aggro holders (**NEO-92**), resets NPC runtime behavior rows to **`idle`** (**NEO-93**), restores dev player combat HP to full (**NEO-95**), and clears in-memory encounter progress/completion/event rows for the dev player across catalog encounters (**NEO-108** Bruno). **404** when disabled. Bruno: `scripts/combat-targets-reset-helper.js` (request scripts use `./scripts/…` relative to the collection).
|
||||
|
||||
## Threat / aggro state (NEO-92)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue