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,
+};