Skip to content

Fix monster fallthrough in UNITS_CanAnimModeUseVelocityModifier - #242

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

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

Conversation

@ResurrectedTrader

@ResurrectedTrader ResurrectedTrader commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

In UNITS_CanAnimModeUseVelocityModifier, the UNIT_MONSTER case picks the mode modulators and then falls through into default: return FALSE;. As a result no monster mode ever reaches UNITS_UpdateRunWalkAnimRateAndVelocity: walking and running monsters keep the default anim rate, and their path velocity is never scaled by STAT_VELOCITYPERCENT / faster run-walk.

1.10f

Inlined in UNITS_UpdateAnimRateAndVelocity (D2Common.0x6FDBF050, #10376). The monster branch does the MONSTATSFLAG_NPC walk/run shortcut, then picks the classic or expansion table and moves on to the modulator tests without returning:

6FDBF2CD  mov     edi, [esp+20h+var_C]
6FDBF2D1  mov     edx, ecx
6FDBF2D3  sub     edx, 0
6FDBF2D6  mov     esi, eax
6FDBF2D8  jz      loc_6FDBF44F                      ; player
6FDBF2DE  dec     edx
6FDBF2DF  jz      loc_6FDBF3A4                      ; monster
6FDBF2E5  test    ecx, ecx
6FDBF2E7  jz      loc_6FDBF3DE
6FDBF2ED  cmp     ecx, 1
6FDBF2F0  jz      loc_6FDBF3DE
6FDBF2F6  push    0BCCh
6FDBF2FB  push    6FDE5CF4h
6FDBF300  push    6FDE5E9Ch
6FDBF305  call    6FDC4352h
6FDBF30A  add     esp, 0Ch
6FDBF30D  push    0FFFFFFFFh
6FDBF30F  call    6FDC446Fh
6FDBF314  cmp     eax, 13h
6FDBF317  jnz     short loc_6FDBF2CD
6FDBF319  sub     ecx, 0
6FDBF31C  jz      short loc_6FDBF365
6FDBF31E  dec     ecx
6FDBF31F  jnz     short loc_6FDBF35B
6FDBF321  mov     ecx, [esp+20h+var_C]
6FDBF325  test    ecx, ecx
6FDBF327  jl      short loc_6FDBF34C
6FDBF329  mov     esi, sgptDataTables
6FDBF32F  cmp     ecx, [esi+0A80h]
6FDBF335  jge     short loc_6FDBF34C
6FDBF337  lea     edx, [ecx+ecx*2]
6FDBF33A  lea     edx, [edx+edx*8]
6FDBF33D  add     edx, edx
6FDBF33F  sub     edx, ecx
6FDBF341  mov     ecx, [esi+0A78h]
6FDBF347  lea     ecx, [ecx+edx*8]
6FDBF34A  jmp     short loc_6FDBF34E
6FDBF34C  xor     ecx, ecx
6FDBF34E  cmp     eax, 0Fh
6FDBF351  jz      short loc_6FDBF35F
6FDBF353  movsx   eax, word ptr [ecx+36h]
6FDBF357  test    eax, eax
6FDBF359  jg      short loc_6FDBF372
6FDBF35B  xor     eax, eax
6FDBF35D  jmp     short loc_6FDBF37E
6FDBF35F  movsx   eax, word ptr [ecx+38h]
6FDBF363  jmp     short loc_6FDBF357
6FDBF365  sub     eax, 3
6FDBF368  neg     eax
6FDBF36A  sbb     eax, eax
6FDBF36C  and     eax, 70h
6FDBF36F  add     eax, 65h
6FDBF372  cmp     eax, 7FFFh
6FDBF377  jl      short loc_6FDBF37E
6FDBF379  mov     eax, 7FFFh
6FDBF37E  mov     edx, [ebp+2Ch]
6FDBF381  push    0CDFh
6FDBF386  push    6FDE5CF4h
6FDBF38B  push    1000h
6FDBF390  push    edx
6FDBF391  mov     [ebp+4Ch], ax
6FDBF395  call    D2Common_10146
6FDBF39A  pop     edi
6FDBF39B  pop     esi
6FDBF39C  pop     ebp
6FDBF39D  pop     ebx
6FDBF39E  add     esp, 10h
6FDBF3A1  retn    0Ch
6FDBF3A4  mov     ecx, [esp+20h+var_C]
6FDBF3A8  call    sub_6FDA68F0                      ; MonStats record
6FDBF3AD  test    eax, eax
6FDBF3AF  jz      short loc_6FDBF41F
6FDBF3B1  mov     edx, ds:gdwBitMasks
6FDBF3B7  xor     ecx, ecx
6FDBF3B9  mov     cl, [eax+0Dh]                     ; MONSTATSFLAG_NPC
6FDBF3BC  and     ecx, [edx]
6FDBF3BE  mov     [esp+20h+var_4], ecx
6FDBF3C2  jz      short loc_6FDBF41F
6FDBF3C4  sub     esi, 2                            ; NPC: walk (2) / run (15) only
6FDBF3C7  jz      loc_6FDBF48E
6FDBF3CD  sub     esi, 0Dh
6FDBF3D0  jz      loc_6FDBF48E
6FDBF3D6  mov     ecx, [esp+20h+var_10]
6FDBF3DA  mov     eax, [esp+20h+arg_0]
6FDBF3DE  mov     edx, ecx
6FDBF3E0  sub     edx, 0
6FDBF3E3  jz      loc_6FDBF63D
6FDBF3E9  dec     edx
6FDBF3EA  jz      loc_6FDBF61C
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
6FDBF41F  cmp     edi, 19Ah                         ; <== monster: class >= 410
6FDBF425  jge     short loc_6FDBF43B
6FDBF427  mov     ecx, [esp+20h+var_10]
6FDBF42B  lea     eax, [esi+esi*4]
6FDBF42E  lea     edx, unk_6FDD2DB8[eax*4]          ; <== classic monster modulators
6FDBF435  mov     eax, [esp+20h+arg_0]
6FDBF439  jmp     short loc_6FDBF459                ; <== no return, go on to the test
6FDBF43B  mov     eax, [esp+20h+arg_0]
6FDBF43F  lea     ecx, [esi+esi*4]
6FDBF442  lea     edx, dword_6FDD2EF8[ecx*4]        ; <== expansion monster modulators
6FDBF449  mov     ecx, [esp+20h+var_10]
6FDBF44D  jmp     short loc_6FDBF459                ; <== no return, go on to the test
6FDBF44F  lea     edx, [eax+eax*4]
6FDBF452  lea     edx, unk_6FDD2C28[edx*4]          ; player modulators
6FDBF459  mov     esi, [edx+4]                      ; bCanUseVelocityModifier
6FDBF45C  test    esi, esi
6FDBF45E  jnz     short loc_6FDBF492                ; -> run/walk rate + path velocity
6FDBF460  cmp     dword ptr [edx], 0                ; bCanUseVelocityModifierByPassiveSkill
6FDBF463  jz      loc_6FDBF3DE
6FDBF469  push    ebp
6FDBF46A  call    D2Common_10323                    ; used skill
6FDBF46F  test    eax, eax
6FDBF471  jz      loc_6FDBF3D6
6FDBF477  push    eax
6FDBF478  call    D2Common_10985                    ; skill flags: passive, not stun
6FDBF47D  test    al, 1
6FDBF47F  jz      loc_6FDBF3D6
6FDBF485  test    ah, 10h
6FDBF488  jnz     loc_6FDBF3D6
6FDBF48E  mov     ecx, [esp+20h+var_10]
6FDBF492  mov     eax, [ebp+2Ch]
6FDBF495  test    eax, eax
6FDBF497  jnz     short loc_6FDBF4C0
6FDBF499  mov     eax, [esp+20h+arg_8]
6FDBF49D  mov     edx, [esp+20h+arg_4]
6FDBF4A1  push    eax
6FDBF4A2  mov     eax, [esp+24h+var_C]
6FDBF4A6  push    edx
6FDBF4A7  push    eax
6FDBF4A8  push    ecx
6FDBF4A9  push    6FDE5E50h
6FDBF4AE  call    6FDC4364h
6FDBF4B3  add     esp, 14h
6FDBF4B6  pop     edi
6FDBF4B7  pop     esi
6FDBF4B8  pop     ebp
6FDBF4B9  pop     ebx
6FDBF4BA  add     esp, 10h
6FDBF4BD  retn    0Ch

1.14d

The same in Game.exe 0x6214A0:

006214A0  push    ebp
006214A1  mov     ebp, esp
006214A3  push    ecx
006214A4  mov     eax, ecx
006214A6  sub     eax, 0
006214A9  jz      loc_621544
006214AF  sub     eax, 1
006214B2  jz      short loc_6214E4                  ; monster
006214B4  test    ecx, ecx
006214B6  jz      loc_621577
006214BC  cmp     ecx, 1
006214BF  jz      loc_621577
006214C5  push    0BD2h
006214CA  call    GetAddress
006214CF  push    eax
006214D0  push    offset Default
006214D5  call    ERROR_UnrecoverableInternalError_Halt
006214DA  add     esp, 0Ch
006214DD  push    0FFFFFFFFh
006214DF  call    _exit
006214E4  test    esi, esi
006214E6  jl      short loc_621527
006214E8  mov     ecx, off_744304
006214EE  cmp     esi, [ecx+0A80h]
006214F4  jge     short loc_621527
006214F6  mov     eax, esi
006214F8  imul    eax, 1A8h
006214FE  add     eax, [ecx+0A78h]
00621504  jz      short loc_621527
00621506  movzx   eax, byte ptr [eax+0Dh]
0062150A  and     eax, ds:dword_6CE268              ; MONSTATSFLAG_NPC
00621510  jz      short loc_621527
00621512  sub     edx, 2
00621515  jz      short loc_62151C
00621517  sub     edx, 0Dh
0062151A  jnz     short loc_621577
0062151C  mov     eax, 1
00621521  mov     esp, ebp
00621523  pop     ebp
00621524  retn    4
00621527  cmp     esi, 19Ah                         ; <== monster: class >= 410
0062152D  lea     eax, [edx+edx*4]
00621530  jge     short loc_62153B
00621532  lea     eax, unk_6E8B90[eax*4]            ; <== classic monster modulators
00621539  jmp     short loc_62154E
0062153B  lea     eax, unk_6E8CD0[eax*4]            ; <== expansion monster modulators
00621542  jmp     short loc_62154E
00621544  lea     eax, [edx+edx*4]
00621547  lea     eax, unk_6E8A00[eax*4]            ; player modulators
0062154E  cmp     dword ptr [eax+4], 0              ; bCanUseVelocityModifier
00621552  jnz     short loc_62151C
00621554  cmp     dword ptr [eax], 0                ; bCanUseVelocityModifierByPassiveSkill
00621557  jz      short loc_621577
00621559  mov     ecx, [ebp+arg_0]
0062155C  push    ecx
0062155D  call    GetCurrentSkill                   ; used skill
00621562  test    eax, eax
00621564  jz      short loc_621577
00621566  push    eax
00621567  call    SKILL_GetFlags
0062156C  test    al, 1
0062156E  jz      short loc_621577
00621570  test    eax, 1000h
00621575  jz      short loc_62151C
00621577  xor     eax, eax
00621579  mov     esp, ebp
0062157B  pop     ebp
0062157C  retn    4

Change

Add the missing break;.

🤖 Generated with Claude Code

The UNIT_MONSTER case selected the mode modulators and then fell
through into "default: return FALSE", so no monster mode ever took
UNITS_UpdateRunWalkAnimRateAndVelocity: walking and running monsters
kept the default anim rate and their path velocity was never scaled.

In 1.10f the check is inlined in UNITS_UpdateAnimRateAndVelocity
(D2Common.0x6FDBF050). The monster branch picks the classic or
expansion table (0x6FDBF41F-0x6FDBF44D) and goes on to test
bCanUseVelocityModifier at 0x6FDBF459 without returning early. 1.14d is
the same (Game.exe.0x6214A0).

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