Skip to content

Fix monster fallthrough in UNITS_CanAnimModeUseAttackRate - #243

Open
ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/units-attack-rate-monster-fallthrough
Open

ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/units-attack-rate-monster-fallthrough

Conversation

@ResurrectedTrader

@ResurrectedTrader ResurrectedTrader commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

UNITS_CanAnimModeUseAttackRate has the same missing break as UNITS_CanAnimModeUseVelocityModifier: the UNIT_MONSTER case falls through into default: return FALSE;, so monster attacks never run at their attack rate (UNITS_UpdateAttackAnimRateAndVelocity).

1.10f

Inlined in UNITS_UpdateAnimRateAndVelocity (D2Common.0x6FDBF050, #10376). The velocity test above falls into this one (0x6FDBF3DE); the monster branch picks the table and goes on to the bCanUseAttackRate test:

6FDBF3DE  mov     edx, ecx
6FDBF3E0  sub     edx, 0
6FDBF3E3  jz      loc_6FDBF63D                      ; player
6FDBF3E9  dec     edx
6FDBF3EA  jz      loc_6FDBF61C                      ; monster
6FDBF3F0  test    ecx, ecx
6FDBF3F2  jz      loc_6FDBF81E
6FDBF3F8  cmp     ecx, 1
6FDBF3FB  jz      loc_6FDBF81E
6FDBF401  push    0BE9h
6FDBF406  push    6FDE5CF4h
6FDBF40B  push    6FDE5E9Ch
6FDBF410  call    6FDC4352h
6FDBF415  add     esp, 0Ch
6FDBF418  push    0FFFFFFFFh
6FDBF41A  call    6FDC446Fh
6FDBF61C  mov     edx, [esp+20h+var_C]
6FDBF620  cmp     edx, 19Ah                         ; <== monster: class >= 410
6FDBF626  lea     edx, [eax+eax*4]
6FDBF629  jge     short loc_6FDBF634
6FDBF62B  lea     edx, unk_6FDD2DB8[edx*4]          ; <== classic monster modulators
6FDBF632  jmp     short loc_6FDBF647                ; <== no return, go on to the test
6FDBF634  lea     edx, dword_6FDD2EF8[edx*4]        ; <== expansion monster modulators
6FDBF63B  jmp     short loc_6FDBF647                ; <== no return, go on to the test
6FDBF63D  lea     edx, [eax+eax*4]
6FDBF640  lea     edx, unk_6FDD2C28[edx*4]          ; player modulators
6FDBF647  mov     esi, [edx+0Ch]                    ; bCanUseAttackRate
6FDBF64A  test    esi, esi
6FDBF64C  jnz     short loc_6FDBF69B                ; -> attack rate
6FDBF64E  mov     esi, [edx+8]                      ; bCanUseSkillAttackRate
6FDBF651  test    esi, esi
6FDBF653  jz      loc_6FDBF81E
6FDBF659  mov     ecx, [ebp+0A8h]
6FDBF65F  call    sub_6FDAFF30                      ; used skill
6FDBF664  test    eax, eax
6FDBF666  jz      loc_6FDBF816
6FDBF66C  push    0BF4h
6FDBF671  push    6FDE5CF4h
6FDBF676  push    eax
6FDBF677  call    D2Common_10963
6FDBF67C  mov     ecx, eax
6FDBF67E  call    sub_6FD9E040
6FDBF683  mov     edx, ds:gdwBitMasks
6FDBF689  xor     ecx, ecx
6FDBF68B  mov     cl, [eax+6]
6FDBF68E  and     ecx, [edx+0Ch]                    ; SKILLSFLAG_USEATTACKRATE
6FDBF691  mov     [esp+20h+arg_8], ecx
6FDBF695  jz      loc_6FDBF816
6FDBF69B  push    1
6FDBF69D  push    ebp
6FDBF69E  call    D2Common_10440                    ; attack rate

1.14d

The same in Game.exe 0x621580:

00621580  push    ebp
00621581  mov     ebp, esp
00621583  mov     eax, edx
00621585  sub     eax, 0
00621588  jz      short loc_6215DB
0062158A  sub     eax, 1
0062158D  jz      short loc_6215BD                  ; monster
0062158F  test    edx, edx
00621591  jz      short loc_6215B7
00621593  cmp     edx, 1
00621596  jz      short loc_6215B7
00621598  push    0BEFh
0062159D  call    GetAddress
006215A2  push    eax
006215A3  push    offset Default
006215A8  call    ERROR_UnrecoverableInternalError_Halt
006215AD  add     esp, 0Ch
006215B0  push    0FFFFFFFFh
006215B2  call    _exit
006215B7  xor     eax, eax
006215B9  pop     ebp
006215BA  retn    8
006215BD  cmp     [ebp+arg_4], 19Ah                 ; <== monster: class >= 410
006215C4  lea     eax, [ecx+ecx*4]
006215C7  jge     short loc_6215D2
006215C9  lea     eax, unk_6E8B90[eax*4]            ; <== classic monster modulators
006215D0  jmp     short loc_6215E5
006215D2  lea     eax, unk_6E8CD0[eax*4]            ; <== expansion monster modulators
006215D9  jmp     short loc_6215E5
006215DB  lea     eax, [ecx+ecx*4]
006215DE  lea     eax, unk_6E8A00[eax*4]            ; player modulators
006215E5  cmp     dword ptr [eax+0Ch], 0            ; bCanUseAttackRate
006215E9  jz      short loc_6215F4
006215EB  mov     eax, 1
006215F0  pop     ebp
006215F1  retn    8

Change

Add the missing break;.

Once monsters reach the skill-attack-rate branch, they can hit the NULL used-skill dereference that is fixed separately in #244. The two should land together.

🤖 Generated with Claude Code

Same missing break as UNITS_CanAnimModeUseVelocityModifier: the
UNIT_MONSTER case fell through into "default: return FALSE", so monster
attacks never ran at their attack rate.

In 1.10f the check is inlined in UNITS_UpdateAnimRateAndVelocity
(D2Common.0x6FDBF050). The monster branch picks the table at
0x6FDBF61C-0x6FDBF63B and tests bCanUseAttackRate at 0x6FDBF647. 1.14d
is the same (Game.exe.0x6215BD).

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