diff --git a/sdk/API.md b/sdk/API.md index 5da72b5dc..3ee61d7ee 100644 --- a/sdk/API.md +++ b/sdk/API.md @@ -534,6 +534,8 @@ interface OpFeedbackState { ```typescript interface BotWorldState { tick: number; + /** Version of nearby-player combat-target telemetry emitted by the Lite client. */ + combatTargetTelemetryVersion?: number; /** Tick on which an updated Lite runner already emitted policy-gated KBD op2. */ fastKbdAttackTick?: number; /** Prayer-on packets emitted in-process immediately before first-spawn KBD op2. */ diff --git a/sdk/types.ts b/sdk/types.ts index 9cce62f2a..ec59cef4f 100644 --- a/sdk/types.ts +++ b/sdk/types.ts @@ -139,6 +139,11 @@ export interface NearbyPlayer { distance: number; /** See NearbyNpc.reachable. */ reachable?: boolean; + /** Current client-observed combat target. This is populated only while the + * target is corroborated by the entity's combat cycle. */ + combatTargetType?: 'npc' | 'player' | 'none'; + combatTargetIndex?: number; + inCombat?: boolean; } /** @@ -552,6 +557,8 @@ export interface OpFeedbackState { export interface BotWorldState { tick: number; + /** Version of nearby-player combat-target telemetry emitted by the Lite client. */ + combatTargetTelemetryVersion?: number; /** Tick on which an updated Lite runner already emitted policy-gated KBD op2. */ fastKbdAttackTick?: number; /** Prayer-on packets emitted in-process immediately before first-spawn KBD op2. */ diff --git a/server/webclient/src/bot/StateCollector.ts b/server/webclient/src/bot/StateCollector.ts index 853d5ebc9..16c5fc55b 100644 --- a/server/webclient/src/bot/StateCollector.ts +++ b/server/webclient/src/bot/StateCollector.ts @@ -164,6 +164,7 @@ export class BotStateCollector implements ScanProvider { return { tick: currentTick, + combatTargetTelemetryVersion: 1, revision: this.publicationRevision, player: this.collectPlayerState(currentTick, clientCycle), skills: this.collectSkills(), @@ -171,7 +172,7 @@ export class BotStateCollector implements ScanProvider { equipment: this.collectInventory(EQUIPMENT_INTERFACE_ID), combatStyle: this.collectCombatStyle(), nearbyNpcs: this.collectNearbyNpcs(clientCycle), - nearbyPlayers: this.collectNearbyPlayers(), + nearbyPlayers: this.collectNearbyPlayers(clientCycle), nearbyLocs: this.collectNearbyLocs(), groundItems: this.collectGroundItems(), gameMessages: this.collectGameMessages(currentTick, publish), @@ -996,7 +997,7 @@ export class BotStateCollector implements ScanProvider { return npcs; } - private collectNearbyPlayers(): NearbyPlayer[] { + private collectNearbyPlayers(clientCycle: number): NearbyPlayer[] { const c = this.client as any; const players: NearbyPlayer[] = []; const localPlayer = c.localPlayer; @@ -1023,6 +1024,10 @@ export class BotStateCollector implements ScanProvider { // Convert fine-grained local coords (128 units/tile) to world coordinates const playerWorldX = baseX + ((player.x || 0) >> 7); const playerWorldZ = baseZ + ((player.z || 0) >> 7); + const facing = BotStateCollector.decodeFaceEntity(player.faceEntity ?? -1); + const combatCycle = player.combatCycle ?? -1000; + const corroborated = combatCycle > clientCycle; + const target = corroborated ? facing : { type: 'none' as const, index: -1 }; players.push({ kind: 'player', @@ -1032,6 +1037,9 @@ export class BotStateCollector implements ScanProvider { x: playerWorldX, z: playerWorldZ, distance, + combatTargetType: target.type, + combatTargetIndex: target.index, + inCombat: corroborated, reachable: this.reachProbeReady() ? this.reachProbe.canReachEntity(player.routeX?.[0] ?? (player.x || 0) >> 7, player.routeZ?.[0] ?? (player.z || 0) >> 7, 1) : undefined diff --git a/server/webclient/src/bot/types.ts b/server/webclient/src/bot/types.ts index de8cb40c6..b0f34db4b 100644 --- a/server/webclient/src/bot/types.ts +++ b/server/webclient/src/bot/types.ts @@ -409,6 +409,8 @@ export interface OpFeedbackState { export interface BotState { tick: number; + /** Version of nearby-player combat-target telemetry emitted by the Lite client. */ + combatTargetTelemetryVersion: number; /** Monotonic publication cursor; advances for each state sent to agents. */ revision: number; player: PlayerState | null;