From af80b878ad1ade50c6921a36f343cd98cca69a01 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 31 May 2026 18:46:00 -0400 Subject: [PATCH] NEO-108: Bruno move near each NPC and isolate pulse slot. Ranged/elite casts failed out_of_range after melee at (-5,-5); add prototype-npc-bruno-helper moves and pulse cooldown between chains. Combat-health uses slot 4 and moves near elite before cast. --- .../Get combat health after elite attack.bru | 18 ++++----- ... encounter progress after defeat spine.bru | 23 ++++++----- .../scripts/prototype-npc-bruno-helper.js | 38 +++++++++++++++++++ 3 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 bruno/neon-sprawl-server/scripts/prototype-npc-bruno-helper.js diff --git a/bruno/neon-sprawl-server/combat-health/Get combat health after elite attack.bru b/bruno/neon-sprawl-server/combat-health/Get combat health after elite attack.bru index faf175a..b953592 100644 --- a/bruno/neon-sprawl-server/combat-health/Get combat health after elite attack.bru +++ b/bruno/neon-sprawl-server/combat-health/Get combat health after elite attack.bru @@ -13,24 +13,24 @@ docs { script:pre-request { const axios = require("axios"); const { resetPrototypeCombatTargets } = require("./scripts/combat-targets-reset-helper.js"); + const { + moveNearPrototypeNpc, + sleep, + } = require("./scripts/prototype-npc-bruno-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" } }; - - function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } + const slotIndex = 4; await resetPrototypeCombatTargets(bru); - await axios.post( - `${baseUrl}/game/players/${playerId}/move`, - { schemaVersion: 1, target: { x: -2, y: 0.9, z: -2 } }, + await moveNearPrototypeNpc( + baseUrl, + playerId, + "prototype_npc_elite", jsonHeaders, ); - const slotIndex = 3; - await axios.post( `${baseUrl}/game/players/${playerId}/hotbar-loadout`, { diff --git a/bruno/neon-sprawl-server/encounter-progress/Get encounter progress after defeat spine.bru b/bruno/neon-sprawl-server/encounter-progress/Get encounter progress after defeat spine.bru index 5a5194f..d715e61 100644 --- a/bruno/neon-sprawl-server/encounter-progress/Get encounter progress after defeat spine.bru +++ b/bruno/neon-sprawl-server/encounter-progress/Get encounter progress after defeat spine.bru @@ -11,15 +11,16 @@ docs { script:pre-request { const axios = require("axios"); const { resetPrototypeCombatTargets } = require("./scripts/combat-targets-reset-helper.js"); + const { + moveNearPrototypeNpc, + PULSE_COOLDOWN_MS, + sleep, + } = require("./scripts/prototype-npc-bruno-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" } }; const slotIndex = 3; - function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); - } - function rowForPocket(body) { const row = body.encounters.find((x) => x.encounterId === "prototype_combat_pocket"); if (!row) { @@ -40,11 +41,13 @@ script:pre-request { } async function defeatNpc(targetId) { + await moveNearPrototypeNpc(baseUrl, playerId, targetId, jsonHeaders); await axios.post( `${baseUrl}/game/players/${playerId}/target/select`, { schemaVersion: 1, targetId }, jsonHeaders, ); + await sleep(PULSE_COOLDOWN_MS); const castBody = { schemaVersion: 1, slotIndex, @@ -59,13 +62,15 @@ script:pre-request { ); const body = response.data; if (!body?.accepted || !body?.combatResolution) { - throw new Error(`cast ${i + 1} vs ${targetId} failed: ${JSON.stringify(body)}`); + throw new Error( + `cast ${i + 1} vs ${targetId} failed (${body?.reasonCode ?? "no_reason"}): ${JSON.stringify(body)}`, + ); } if (body.combatResolution.targetDefeated) { return; } if (i < 3) { - await sleep(3200); + await sleep(PULSE_COOLDOWN_MS); } } throw new Error(`did not defeat ${targetId} within 4 pulses`); @@ -73,12 +78,6 @@ script:pre-request { await resetPrototypeCombatTargets(bru); - await axios.post( - `${baseUrl}/game/players/${playerId}/move`, - { schemaVersion: 1, target: { x: -5, y: 0.9, z: -5 } }, - jsonHeaders, - ); - await axios.post( `${baseUrl}/game/players/${playerId}/hotbar-loadout`, { diff --git a/bruno/neon-sprawl-server/scripts/prototype-npc-bruno-helper.js b/bruno/neon-sprawl-server/scripts/prototype-npc-bruno-helper.js new file mode 100644 index 0000000..2638715 --- /dev/null +++ b/bruno/neon-sprawl-server/scripts/prototype-npc-bruno-helper.js @@ -0,0 +1,38 @@ +const axios = require("axios"); + +/** Matches server anchors + AbilityCastApiTests move offset. */ +const PROTOTYPE_NPC_ANCHORS = { + prototype_npc_melee: { x: -3, y: 0.5, z: -3 }, + prototype_npc_ranged: { x: 3, y: 0, z: 3 }, + prototype_npc_elite: { x: 0, y: 0.5, z: 0 }, +}; + +/** prototype_pulse catalog cooldown (seconds) — real-time Bruno waits. */ +const PULSE_COOLDOWN_MS = 3200; + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function moveNearPrototypeNpc(baseUrl, playerId, targetId, jsonHeaders) { + const anchor = PROTOTYPE_NPC_ANCHORS[targetId]; + if (!anchor) { + throw new Error(`unknown prototype npc: ${targetId}`); + } + + await axios.post( + `${baseUrl}/game/players/${playerId}/move`, + { + schemaVersion: 1, + target: { x: anchor.x - 1, y: 0.9, z: anchor.z - 1 }, + }, + jsonHeaders, + ); +} + +module.exports = { + PROTOTYPE_NPC_ANCHORS, + PULSE_COOLDOWN_MS, + moveNearPrototypeNpc, + sleep, +};