Skip to content

Scan target list slot 8 in sub_6FCF2110 - #255

Open
ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/ai-target-scan-slot-8
Open

ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/ai-target-scan-slot-8

Conversation

@ResurrectedTrader

Copy link
Copy Markdown
Contributor

In the evil-alignment branch of sub_6FCF2110, after the eight player slots, the game walks one more target list, pGame->pTargetNodes[8]. MOO walked whatever pTargetNode was left over from the player loop (slot 7's head, or nullptr), so the slot 8 list was never scanned.

The game loads the next slot's head at the end of every player-slot iteration, so after slot 7 the pointer holds slot 8.

1.10f

D2Game.0x6FCF2110, Hex-Rays (a1 = pGame, 4344 = 0x10F8 = pTargetNodes):

v12 = *(int ***)(a1 + 4344);               // pTargetNodes[0]
v69 = (int *)(a1 + 4344);
...
for ( i = 0; i < 8; ++i )
{
    if ( v12 != nullptr )
    {
        ...                                // player slot i
    }
    v12 = (int **)*++v69;                  // <== next slot's head
}
for ( ; v12 != nullptr; v12 = (int **)v12[2] )   // <== slot 8
{
    v37 = *v12;
    if ( v62 == *((_BYTE *)*v12 + 24) )
    {
        ...
        if ( v42 / 2 < v63 && (v65 == 0 || D2Common_10362(v66, v37, 4) == 0) )
        {
            v63 = v43 / 2;
            v64 = v37;
        }
    }
}
v44 = v69;
v69 = nullptr;
v45 = 0x7FFFFFFF;
v46 = (int **)v44[1];                      // slot 9

The slot pointer setup, the loop tail and what follows:

6FCF21A2  mov     ebp, [edi+10F8h]                  ; pTargetNodes[0]
6FCF21A8  lea     eax, [edi+10F8h]                  ; &pTargetNodes[0]
6FCF21AE  xor     esi, esi
6FCF21B0  mov     [esp+2Ch], eax
...
6FCF246E  mov     eax, [esp+2Ch]                    ; every player-slot path ends here
6FCF2472  add     eax, 4
6FCF2475  mov     [esp+2Ch], eax
6FCF2479  mov     ebp, [eax]                        ; <== next slot's head
6FCF247B  mov     eax, [esp+38h]                    ; slot counter
6FCF247F  inc     eax
6FCF2480  cmp     eax, 8
6FCF2483  mov     [esp+38h], eax
6FCF2487  jl      6FCF22E7
6FCF248D  test    ebp, ebp                          ; <== walk slot 8
6FCF248F  jz      6FCF2551
6FCF2495  mov     edi, [ebp+0]
6FCF2498  mov     al, [esp+13h]
6FCF249C  cmp     al, [edi+18h]                     ; same act
6FCF249F  jnz     6FCF2546
...
6FCF2546  mov     ebp, [ebp+8]                      ; pNext
6FCF2549  test    ebp, ebp
6FCF254B  jnz     6FCF2495
6FCF2551  mov     eax, [esp+2Ch]                    ; &pTargetNodes[8]
6FCF2555  mov     dword ptr [esp+2Ch], 0
6FCF255D  mov     ebx, 7FFFFFFFh
6FCF2562  mov     ebp, [eax+4]                      ; pTargetNodes[9]

1.14d

MONSTERAI_FindBestTarget (Game.exe 0x005DD7F0), Hex-Rays, the same structure:

v37 = a1 + 4344;
v10 = *(int **)(a1 + 4344);
...
for ( i = 0; i < 8; ++i )
{
    ...
    v10 = *(int **)(v37 + 4);              // <== next slot's head
    v37 += 4;
}
for ( ; v10 != nullptr; v10 = (int *)v10[2] )   // <== slot 8
{
    ...
}
v36 = *(int **)(v37 + 4);                  // slot 9
005DDA1D  mov     eax, [ebp+var_24]
005DDA20  mov     ebx, [eax+4]                      ; <== next slot's head
005DDA23  add     eax, 4
005DDA26  mov     [ebp+var_24], eax
005DDA29  mov     eax, [ebp+var_2C]
005DDA2C  add     eax, 1
005DDA2F  cmp     eax, 8
005DDA32  mov     [ebp+var_2C], eax
005DDA35  jl      loc_5DD8E0
005DDA3B  test    ebx, ebx                          ; <== walk slot 8
005DDA3D  jz      short loc_5DDA85

Change

Load pGame->pTargetNodes[8] before the slot 8 loop. Not version-gated, since 1.10f and 1.14d agree. Also adds the 1.14d address.

🤖 Generated with Claude Code

After the eight player slots, the evil-alignment branch of
sub_6FCF2110 walks a further target list. MOO walked whatever
pTargetNode was left over from the player loop (slot 7's head, or
nullptr), so the slot 8 list was never scanned.

The game loads the next slot's head at the end of every player-slot
iteration, so after slot 7 it holds pGame->pTargetNodes[8].

1.10f (D2Game.0x6FCF2110), Hex-Rays (a1 = pGame, 4344 = 0x10F8 =
pTargetNodes):

    v12 = *(int ***)(a1 + 4344);           // pTargetNodes[0]
    v69 = (int *)(a1 + 4344);
    ...
    for ( i = 0; i < 8; ++i )
    {
        ...
        v12 = (int **)*++v69;              // next slot's head
    }
    for ( ; v12 != nullptr; v12 = (int **)v12[2] )   // slot 8
    {
        ...
    }
    v44 = v69;
    v46 = (int **)v44[1];                  // slot 9

Disassembly:

    6FCF21A2  mov  ebp, [edi+10F8h]    ; pTargetNodes[0]
    6FCF21A8  lea  eax, [edi+10F8h]    ; &pTargetNodes[0]
    6FCF21B0  mov  [esp+2Ch], eax
    ...
    6FCF246E  mov  eax, [esp+2Ch]      ; every player-slot path ends here
    6FCF2472  add  eax, 4
    6FCF2475  mov  [esp+2Ch], eax
    6FCF2479  mov  ebp, [eax]          ; next slot's head
    6FCF247B  mov  eax, [esp+38h]      ; slot counter
    6FCF247F  inc  eax
    6FCF2480  cmp  eax, 8
    6FCF2487  jl   6FCF22E7
    6FCF248D  test ebp, ebp            ; walk slot 8
    6FCF248F  jz   6FCF2551
    ...
    6FCF2551  mov  eax, [esp+2Ch]      ; &pTargetNodes[8]
    6FCF2562  mov  ebp, [eax+4]        ; pTargetNodes[9], the list that follows

1.14d (Game.exe 0x005DD7F0) is the same: each iteration ends with
v10 = *(int **)(v37 + 4); v37 += 4; (0x005DDA20 mov ebx, [eax+4]),
then the bare for ( ; v10 != nullptr; ...) loop at 0x005DDA3B.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant