101 lines
3.9 KiB
C#
101 lines
3.9 KiB
C#
using NeonSprawl.Server.Game.Combat;
|
|
|
|
namespace NeonSprawl.Server.Game.Npc;
|
|
|
|
/// <summary>Maps <c>GET /game/world/npc-runtime-snapshot</c> (NEO-94).</summary>
|
|
public static class NpcRuntimeSnapshotWorldApi
|
|
{
|
|
public static WebApplication MapNpcRuntimeSnapshotWorldApi(this WebApplication app)
|
|
{
|
|
app.MapGet(
|
|
"/game/world/npc-runtime-snapshot",
|
|
(
|
|
TimeProvider clock,
|
|
INpcRuntimeStateStore runtimeStore,
|
|
IThreatStateStore threatStore,
|
|
INpcBehaviorDefinitionRegistry behaviorRegistry,
|
|
ICombatEntityHealthStore combatHealthStore,
|
|
IPlayerCombatHealthStore playerHealthStore) =>
|
|
{
|
|
var now = clock.GetUtcNow();
|
|
NpcRuntimeOperations.AdvanceAll(
|
|
now,
|
|
runtimeStore,
|
|
threatStore,
|
|
behaviorRegistry,
|
|
combatHealthStore,
|
|
playerHealthStore);
|
|
return Results.Json(BuildSnapshot(now, runtimeStore, threatStore, behaviorRegistry));
|
|
});
|
|
|
|
return app;
|
|
}
|
|
|
|
internal static NpcRuntimeSnapshotResponse BuildSnapshot(
|
|
DateTimeOffset nowUtc,
|
|
INpcRuntimeStateStore runtimeStore,
|
|
IThreatStateStore threatStore,
|
|
INpcBehaviorDefinitionRegistry behaviorRegistry)
|
|
{
|
|
var ids = PrototypeNpcRegistry.GetInstanceIdsInOrder();
|
|
var instances = new List<NpcInstanceRuntimeJson>(ids.Count);
|
|
foreach (var npcInstanceId in ids)
|
|
{
|
|
if (!PrototypeNpcRegistry.TryGet(npcInstanceId, out var entry))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Prototype NPC instance '{npcInstanceId}' is missing from {nameof(PrototypeNpcRegistry)}.");
|
|
}
|
|
|
|
if (!behaviorRegistry.TryGetDefinition(entry.BehaviorDefId, out var behavior))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Behavior definition '{entry.BehaviorDefId}' for NPC '{npcInstanceId}' is missing from {nameof(INpcBehaviorDefinitionRegistry)}.");
|
|
}
|
|
|
|
if (!runtimeStore.TryGet(npcInstanceId, out var runtime))
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Prototype NPC runtime row '{npcInstanceId}' is registered but missing from {nameof(INpcRuntimeStateStore)}.");
|
|
}
|
|
|
|
string? aggroHolderPlayerId = null;
|
|
if (threatStore.TryGet(npcInstanceId, out var threat))
|
|
{
|
|
aggroHolderPlayerId = threat.AggroHolderPlayerId;
|
|
}
|
|
|
|
ActiveTelegraphJson? activeTelegraph = null;
|
|
if (runtime.BehaviorState == NpcBehaviorState.TelegraphWindup &&
|
|
runtime.ActiveTelegraph is { } telegraph)
|
|
{
|
|
var elapsedSeconds = (nowUtc - telegraph.WindupStartedUtc).TotalSeconds;
|
|
var remainingSeconds = Math.Max(0, behavior.TelegraphWindupSeconds - elapsedSeconds);
|
|
activeTelegraph = new ActiveTelegraphJson
|
|
{
|
|
TelegraphId = telegraph.TelegraphId,
|
|
NpcInstanceId = npcInstanceId,
|
|
WindupRemainingSeconds = remainingSeconds,
|
|
ArchetypeKind = behavior.ArchetypeKind,
|
|
};
|
|
}
|
|
|
|
instances.Add(
|
|
new NpcInstanceRuntimeJson
|
|
{
|
|
NpcInstanceId = npcInstanceId,
|
|
BehaviorDefId = entry.BehaviorDefId,
|
|
State = NpcBehaviorStateWire.ToWireName(runtime.BehaviorState),
|
|
AggroHolderPlayerId = aggroHolderPlayerId,
|
|
ActiveTelegraph = activeTelegraph,
|
|
});
|
|
}
|
|
|
|
return new NpcRuntimeSnapshotResponse
|
|
{
|
|
ServerTimeUtc = nowUtc,
|
|
NpcInstances = instances,
|
|
};
|
|
}
|
|
}
|