agent (ag)
ag is the struct arcdps passes as the src and dst parameters of the
combat / combat_local callbacks (see the
combat callback reference).
Unlike cbtevent, ag is defined directly in the API README
(https://www.deltaconnected.com/arcdps/api/README.txt):
typedef struct ag { const char* name; uintptr_t id; uint32_t prof; uint32_t elite; uint32_t self; uint16_t team;} ag;Field reference
| Offset | C type | Name | Meaning |
|---|---|---|---|
| 0 | const char* | name | Agent display name. In the ev == null agent-list-event convention (see below), a newly added agent’s src->name is the character name and dst->name is the account name. |
| 8 | uintptr_t | id | Agent id. In the ev == null convention, an added agent’s id is src->id; a removed agent’s id is also src->id; a newly targeted agent’s id is src->id (see below for how to distinguish these three cases). |
| 16 | uint32_t | prof | Profession id. Populated as dst->prof in the “agent added” convention. The README does not enumerate profession id values here — see the enum reference for what is/isn’t documented. |
| 20 | uint32_t | elite | Elite specialization id — populated as dst->elite in the “agent added” convention. Overloaded meaning on src during ev == null events: src->elite == 1 signals a “new targeted agent” event rather than an add/remove (see below). |
| 24 | uint32_t | self | Is-self flag — populated as dst->self in the “agent added” convention. Exact truthy encoding (e.g. 0/1 vs. any-nonzero) is not spelled out by the README — undocumented beyond “is self”. |
| 28 | uint16_t | team | Context-dependent: as src->team in the “agent added” convention it is the team id; as dst->team in that same convention it is the subgroup. The README uses team for both meanings depending on which struct instance (src vs. dst) and event context it appears in — undocumented beyond those two call sites. |
Total size is 32 bytes on x64: the field widths sum to 30 bytes
(8 + 8 + 4 + 4 + 4 + 2), and standard C struct alignment inserts 2 bytes
of trailing padding after team so the struct’s size is a multiple of
its 8-byte alignment (driven by the const char* / uintptr_t
members). This padding is compiler-inserted, not a named field, and is
not stated explicitly by the source docs — treat it as derived from
standard x64 alignment rules, not an official offset table.
ev == null agent-list events
The README documents an overloaded use of ag when the combat callback
fires with ev == null — at that point src/dst don’t describe a
combat event, they describe an agent-list change:
- If
src->elite == 1, thensrc->idis the id of the newly targeted agent. - Else, if
src->profis set,src->idwas added:src->name— character namedst->name— account namesrc->id— agent iddst->id— instance id on the mapdst->prof— professiondst->elite— elite specdst->self— is-self flagsrc->team— team iddst->team— subgroup
- Else,
src->idwas removed.
This is also documented on the combat callback reference.
FFI mappings
Rust
#[repr(C)]pub struct Ag { pub name: *const std::os::raw::c_char, pub id: usize, // uintptr_t pub prof: u32, pub elite: u32, pub self_: u32, // `self` is a Rust keyword pub team: u16,}Python (ctypes)
import ctypes
class Ag(ctypes.Structure): _fields_ = [ ("name", ctypes.c_char_p), ("id", ctypes.c_void_p), # uintptr_t ("prof", ctypes.c_uint32), ("elite", ctypes.c_uint32), ("self_", ctypes.c_uint32), # `self` shadows the instance arg name ("team", ctypes.c_uint16), ]Node.js (koffi)
const koffi = require("koffi");
const ag = koffi.struct("ag", { name: "str", // const char* id: "uintptr_t", prof: "uint32_t", elite: "uint32_t", self: "uint32_t", team: "uint16_t",});Classifying an agent in practice
Whether an agent is a player, an NPC, or a gadget is decided by the same
rule the EVTC format documents verbatim for the agent table (there the
fields are named evtc_agent.is_elite and evtc_agent.prof; on ag
the same two concepts are elite and prof, and the
enum reference records
that elite == 0xFFFFFFFF marks a non-player agent for both). See the
EVTC agent classification rules
for the source text.
enum agent_kind { AGENT_PLAYER, AGENT_NPC, AGENT_GADGET };
enum agent_kind classify(const ag* a, uint16_t* out_id) { if (a->elite == 0xFFFFFFFF) { if ((a->prof >> 16) == 0xFFFF) { /* gadget: pseudo id in the low half of prof (volatile — may collide with npc ids, treat separately). */ *out_id = (uint16_t)(a->prof & 0xFFFF); return AGENT_GADGET; } /* npc: species id in the low half of prof (reliable id). */ *out_id = (uint16_t)(a->prof & 0xFFFF); return AGENT_NPC; } /* player: prof is the profession id, elite is the elite spec id. */ return AGENT_PLAYER;}For players, map prof through the
profession id table and
elite through the game’s specialization ids. The WvW allies/enemies
guide applies exactly this split — see
Telling allies from enemies in WvW.
See also
- Combat callback — how
ag*reaches your extension, and the fullev == nullconvention. cbtevent— the accompanying event struct.