Appearance
aowlspt_botcap.h
Source: abi/aowlspt_botcap.h — 185 lines, 6 file-scope functions.
What this header owns
Reproduced verbatim from the header's own banner comment — these notes are frequently the only written record of why the subsystem is shaped the way it is.
text
aowlspt_botcap.h -- OFFLINE SCAV-CAP LIFT for the post-1.0 EFT host.
One Unity-thread int32 field poke that neutralises the offline bot cap so the
generated scav wave pool actually places. It applies the SAME proven,
reflection-free raw-field-write capability as the version brand and the
settings/botdiag probes (static-RVA detour, `aowl_is_readable`-guarded raw
hops, VirtualQuery-guarded write, flag-gated, fail-safe) -- no runtime_invoke,
no reflection (both are DEAD on this build).
## The gate (RE, byte-verified against build 1.1.0.1.46777)
`EFT.BotSpawner.MaxBots` = int32 at offset **+0xC0** on the live BotSpawner.
Verified by the property accessors:
get_MaxBots @0x698410 = `8B 81 C0 00 00 00` (mov eax,[rcx+0xC0])
set_MaxBots @0x698420 = `89 91 C0 00 00 00` (mov [rcx+0xC0],edx)
`BotSpawner::CheckOnMax` computes room = MaxBots - (alive@+0x7C + loading@+0x70);
room<=0 defers the bot forever. **MaxBots==0 means UNLIMITED** (CheckOnMax's
first test short-circuits to allow-all). Bosses carry IgnoreMaxBots and bypass
the cap; scav waves do NOT, so Tagilla+escort consume the small offline
MaxBots and the 24 assault never place. Writing MaxBots=0 lifts the cap.
## What it hooks -- primary: `EFT.BotSpawner::AddPlayer` @ RVA 0x2563AE0
Called EXACTLY ONCE from `<LocalBotsSpawnInitialization>d__12::MoveNext`
(+0xad3fad) at raid init, with RCX = the live BotSpawner (`this`). One-shot,
minimal hot-path work: set `[BotSpawner+0xC0] = 0` once, before the waves
fire. That BotSpawner is the same object CheckOnMax reads, so RCX=BotSpawner
here is confirmed by the accessor form ([rcx+0xC0]).
Prologue @ 0x2563AE0 (byte-verified) -- clean whole-instruction steal, no
RIP-relative operand and no branch in the stolen region:
48 89 5C 24 18 mov [rsp+0x18], rbx (5)
57 push rdi (1)
48 83 EC 20 sub rsp, 0x20 (4)
80 3D 99 F6 B5 04 .. cmp byte [rip+0x4B5F699], 0 (disp32 pins the build)
The first three instructions (10 bytes) relocate cleanly; the `cmp`'s disp32
is included in the signature only to pin the build, not stolen. On any other
build the bytes differ, the guard fails, and NULL is returned -- a missed
bind, never a corrupted game. (CheckOnMax @0x2560940 is the belt-and-suspenders
alternative -- runs before every check -- but fires very frequently; AddPlayer
is preferred for the one-shot minimal write.)
Fail-safe throughout: prologue verified before the pointer is handed out, the
BotSpawner pointer and the +0xC0 slot are guarded before the write, only an
int32 is written, and the whole detour body runs under the VEH/SEH guard so a
fault can never reach the game. A low-risk write (it only relaxes an int
compare the game already performs) but guarded anyway -- it runs during raid
entry, a fragile path.Constants
AOWLSPT_BOTCAP_HAOWL_BC_MAXBOTS_OFFAOWL_BOTCAP_TARGET_COUNT
Types
struct AowlBotCapTarget
Functions
| Signature | Line |
|---|---|
int32_t aowl_bc_off_maxbots(void) | 61 |
void aowl_botcap_target_at(int32_t i) | 96 |
char aowl_botcap_target_name(int32_t i) | 122 |
int32_t aowl_botcap_target_count(void) | 127 |
int32_t aowl_botcap_read_i32(void* p, int32_t off, int32_t* ok) | 132 |
int32_t aowl_botcap_write_i32(void* p, int32_t off, int32_t value) | 162 |

