feat(webclient): publish nearby players' combat targets

NearbyPlayer gains combatTargetType/combatTargetIndex/inCombat, populated only
while the player's combatCycle corroborates the faced entity. BotState carries
combatTargetTelemetryVersion: 1 so consumers can tell supporting clients apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
max bittkerandClaude Opus 5.5 committed 2026-10-18 09:30:48 -04:00
1 parent 578f12fd75
commit 54f3e62084
4 files changed
+21 -2

No files matched your search

+2
View File
@@ -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. */
+7
View File
@@ -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. */
+10 -2
View File
@@ -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
+2
View File
@@ -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;