Skip to content

Fix the warp exclusion in sub_6FC66260 - #250

Open
ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/monster-random-spawn-warp-gate
Open

ResurrectedTrader wants to merge 1 commit into
ThePhrozenKeep:masterfrom
ResurrectedTrader:fix/monster-random-spawn-warp-gate

Conversation

@ResurrectedTrader

Copy link
Copy Markdown
Contributor

sub_6FC66260 picks up to 20 random positions in a room to place a monster. With a7 set, it skips candidates that lie within Levels.txt WarpDist of a warp or of the level's spawn point.

MOO inverts that check. It initialises bSpawnMonster = 0, sets it to 1 when the candidate is near a warp or the spawn point, and only places the monster when it is 1. So:

  • with a7 == 0, every candidate is rejected and the function always returns 0,
  • with a7 != 0, only candidates next to a warp or the spawn point are accepted,
  • a candidate near the spawn point breaks out of the 20-attempt loop instead of trying the next one.

1.10f

D2Game.0x6FC66260, from the candidate roll to the end of the loop ([esp+0x7c] is a7, [esp+0x44] the attempt counter, ebp / ebx the candidate X / Y):

6FC66337  mov     ecx, dword ptr [esp + 0x14]       ; attempt loop
6FC6633B  test    ecx, ecx
6FC6633D  jg      0x6fc66343
6FC6633F  xor     edx, edx
6FC66341  jmp     0x6fc66377
6FC66343  mov     ebx, dword ptr [esi + 4]
6FC66346  mov     eax, dword ptr [esi]
6FC66348  lea     edx, [ecx - 1]
6FC6634B  xor     ebp, ebp
6FC6634D  test    ecx, edx
6FC6634F  mov     edx, 0x6ac690c5
6FC66354  je      0x6fc66367
6FC66356  mul     edx
6FC66358  add     eax, ebx
6FC6635A  adc     edx, ebp
6FC6635C  mov     dword ptr [esi], eax
6FC6635E  mov     dword ptr [esi + 4], edx
6FC66361  xor     edx, edx
6FC66363  div     ecx
6FC66365  jmp     0x6fc66377
6FC66367  mul     edx
6FC66369  add     eax, ebx
6FC6636B  adc     edx, ebp
6FC6636D  mov     dword ptr [esi], eax
6FC6636F  mov     dword ptr [esi + 4], edx
6FC66372  lea     edx, [ecx - 1]
6FC66375  and     edx, eax
6FC66377  mov     eax, dword ptr [esp + 0x28]
6FC6637B  lea     ebp, [edx + eax]                  ; nX
6FC6637E  mov     eax, dword ptr [esp + 0x10]
6FC66382  test    eax, eax
6FC66384  mov     dword ptr [esp + 0x30], ebp
6FC66388  jg      0x6fc6638e
6FC6638A  xor     edx, edx
6FC6638C  jmp     0x6fc663c6
6FC6638E  lea     ecx, [eax - 1]
6FC66391  xor     ebx, ebx
6FC66393  test    eax, ecx
6FC66395  mov     ecx, dword ptr [esi + 4]
6FC66398  mov     eax, dword ptr [esi]
6FC6639A  mov     edx, 0x6ac690c5
6FC6639F  je      0x6fc663b4
6FC663A1  mul     edx
6FC663A3  add     eax, ecx
6FC663A5  adc     edx, ebx
6FC663A7  mov     dword ptr [esi], eax
6FC663A9  mov     dword ptr [esi + 4], edx
6FC663AC  xor     edx, edx
6FC663AE  div     dword ptr [esp + 0x10]
6FC663B2  jmp     0x6fc663c6
6FC663B4  mul     edx
6FC663B6  add     eax, ecx
6FC663B8  adc     edx, ebx
6FC663BA  mov     dword ptr [esi], eax
6FC663BC  mov     dword ptr [esi + 4], edx
6FC663BF  mov     edx, dword ptr [esp + 0x10]
6FC663C3  dec     edx
6FC663C4  and     edx, eax
6FC663C6  mov     eax, dword ptr [esp + 0x2c]
6FC663CA  lea     ebx, [edx + eax]                  ; nY
6FC663CD  mov     eax, dword ptr [esp + 0x7c]       ; <== a7
6FC663D1  test    eax, eax
6FC663D3  je      0x6fc664c4                        ; <== no check -> place
6FC663D9  test    edi, edi                          ; pRoom
6FC663DB  je      0x6fc664c4
6FC663E1  push    edi
6FC663E2  call    0x6fd1b65c                        ; DUNGEON_GetLevelIdFromRoom
6FC663E7  test    eax, eax
6FC663E9  mov     dword ptr [esp + 0x34], eax
6FC663ED  je      0x6fc664c4
6FC663F3  push    edi
6FC663F4  call    0x6fd1bfe0                        ; DUNGEON_GetWarpCoordinatesFromRoom
6FC663F9  mov     edi, eax
6FC663FB  test    edi, edi
6FC663FD  je      0x6fc664c0
6FC66403  mov     ecx, dword ptr [esp + 0x34]
6FC66407  push    ecx
6FC66408  call    0x6fd1bef0                        ; DATATBLS_GetLevelsTxtRecord
6FC6640D  mov     edx, dword ptr [eax + 0xc]        ; dwWarpDist
6FC66410  mov     eax, dword ptr [edi + 0x48]
6FC66413  mov     dword ptr [esp + 0x38], edx
6FC66417  xor     edx, edx
6FC66419  test    eax, eax
6FC6641B  jle     0x6fc66450
6FC6641D  mov     ecx, dword ptr [edi + edx*4]      ; warp X
6FC66420  mov     eax, ebp
6FC66422  mov     ebp, dword ptr [edi + edx*4 + 0x24] ; warp Y
6FC66426  sub     eax, ecx
6FC66428  mov     ecx, ebx
6FC6642A  sub     ecx, ebp
6FC6642C  mov     ebp, ecx
6FC6642E  imul    ebp, ecx
6FC66431  mov     ecx, eax
6FC66433  imul    ecx, eax
6FC66436  mov     eax, dword ptr [esp + 0x38]
6FC6643A  add     ebp, ecx
6FC6643C  cmp     ebp, eax                          ; dist^2 < WarpDist
6FC6643E  jl      0x6fc66534                        ; <== near a warp -> next attempt
6FC66444  mov     eax, dword ptr [edi + 0x48]
6FC66447  mov     ebp, dword ptr [esp + 0x30]
6FC6644B  inc     edx
6FC6644C  cmp     edx, eax
6FC6644E  jl      0x6fc6641d                        ; next warp
6FC66450  lea     edx, [esp + 0x1c]
6FC66454  lea     eax, [esp + 0x18]
6FC66458  push    edx
6FC66459  push    eax
6FC6645A  mov     eax, dword ptr [esp + 0x3c]
6FC6645E  push    0xb
6FC66460  push    eax
6FC66461  push    eax
6FC66462  call    0x6fd1b764                        ; DUNGEON_FindActSpawnLocation
6FC66467  and     eax, 0xff
6FC6646C  mov     ecx, dword ptr [esp + 0x30]
6FC66470  mov     edx, dword ptr [ecx + eax*4 + 0xbc]
6FC66477  push    edx
6FC66478  call    0x6fd1bfda
6FC6647D  mov     eax, dword ptr [esp + 0x18]
6FC66481  test    eax, eax
6FC66483  jle     0x6fc664c0
6FC66485  mov     eax, dword ptr [esp + 0x1c]
6FC66489  test    eax, eax
6FC6648B  jle     0x6fc664c0
6FC6648D  lea     eax, [esp + 0x1c]
6FC66491  lea     ecx, [esp + 0x18]
6FC66495  push    eax
6FC66496  push    ecx
6FC66497  call    0x6fd1bfe6                        ; DUNGEON_GameTileToSubtileCoords
6FC6649C  mov     edi, dword ptr [esp + 0x18]
6FC664A0  mov     ecx, ebx
6FC664A2  mov     edx, dword ptr [esp + 0x1c]
6FC664A6  mov     eax, ebp
6FC664A8  sub     ecx, edx
6FC664AA  sub     eax, edi
6FC664AC  mov     edx, ecx
6FC664AE  imul    edx, ecx
6FC664B1  mov     ecx, eax
6FC664B3  imul    ecx, eax
6FC664B6  mov     eax, dword ptr [esp + 0x38]
6FC664BA  add     edx, ecx
6FC664BC  cmp     edx, eax                          ; dist^2 < WarpDist
6FC664BE  jl      0x6fc66534                        ; <== near the spawn point -> next attempt
6FC664C0  mov     edi, dword ptr [esp + 0x24]       ; <== not near anything -> place
6FC664C4  mov     eax, dword ptr [esp + 0x3c]       ; place: pRoomCoordList?
6FC664C8  test    eax, eax
6FC664CA  je      0x6fc66516
6FC664CC  push    ebx
6FC664CD  push    ebp
6FC664CE  push    edi
6FC664CF  call    0x6fd1bfd4
6FC664D4  cmp     eax, dword ptr [esp + 0x40]
6FC664D8  jne     0x6fc66538
6FC664DA  mov     edx, dword ptr [esp + 0x70]
6FC664DE  mov     eax, dword ptr [esp + 0x6c]
6FC664E2  mov     ecx, dword ptr [esp + 0x20]
6FC664E6  push    1
6FC664E8  push    -1
6FC664EA  push    1
6FC664EC  push    edx
6FC664ED  push    ebx
6FC664EE  push    ebp
6FC664EF  push    eax
6FC664F0  mov     edx, edi
6FC664F2  call    0x6fc6a030                        ; sub_6FC6A030
6FC664F7  test    eax, eax
6FC664F9  je      0x6fc66538
6FC664FB  mov     edx, dword ptr [esp + 0x74]
6FC664FF  mov     eax, dword ptr [esp + 0x78]
6FC66503  pop     edi
6FC66504  pop     esi
6FC66505  mov     dword ptr [edx], ebp              ; *pX = nX
6FC66507  mov     dword ptr [eax], ebx              ; *pY = nY
6FC66509  pop     ebp
6FC6650A  mov     eax, 1
6FC6650F  pop     ebx
6FC66510  add     esp, 0x58
6FC66513  ret     0x14
6FC66516  mov     ecx, dword ptr [esp + 0x70]
6FC6651A  push    1
6FC6651C  push    -1
6FC6651E  push    1
6FC66520  push    ecx
6FC66521  mov     ecx, dword ptr [esp + 0x30]
6FC66525  push    ebx
6FC66526  push    ebp
6FC66527  mov     edx, edi
6FC66529  call    0x6fc69f10                        ; D2GAME_SpawnMonster_6FC69F10
6FC6652E  test    eax, eax
6FC66530  jne     0x6fc664fb
6FC66532  jmp     0x6fc66538
6FC66534  mov     edi, dword ptr [esp + 0x24]
6FC66538  mov     eax, dword ptr [esp + 0x44]       ; next attempt
6FC6653C  inc     eax
6FC6653D  cmp     eax, 0x14                         ; 20 attempts
6FC66540  mov     dword ptr [esp + 0x44], eax
6FC66544  jl      0x6fc66337
6FC6654A  pop     edi
6FC6654B  pop     esi
6FC6654C  pop     ebp
6FC6654D  xor     eax, eax
6FC6654F  pop     ebx
6FC66550  add     esp, 0x58
6FC66553  ret     0x14

1.14d

SPAWN_FindRandomPositionForMonster (Game.exe 0x54DC40) moves both proximity tests into a helper, CheckWarpSpawnCoords (0x54DB50), which returns 1 when the candidate is near a warp or the spawn point:

0054DD1A  cmp     [ebp+arg_10], 0                   ; <== a7
0054DD1E  mov     eax, [ebp+var_24]
0054DD21  lea     edi, [edx+eax]
0054DD24  jz      short loc_54DD38                  ; <== no check -> place
0054DD26  mov     ecx, [ebp+var_C]
0054DD29  mov     eax, [ebp+var_4]
0054DD2C  push    edi
0054DD2D  push    ebx
0054DD2E  push    ecx
0054DD2F  call    CheckWarpSpawnCoords              ; near a warp or the spawn point?
0054DD34  test    eax, eax
0054DD36  jnz     short loc_54DD86                  ; <== yes -> next attempt
0054DD38  cmp     [ebp+var_10], 0                   ; <== no -> place: pRoomCoordList?
0054DD3C  jz      short loc_54DD6B
0054DD3E  mov     edx, [ebp+var_4]
0054DD41  push    edi
0054DD42  push    ebx
0054DD43  push    edx
0054DD44  call    GetGridIndex
0054DD49  cmp     eax, [ebp+var_14]
0054DD4C  jnz     short loc_54DD86
0054DD4E  mov     eax, [ebp+arg_4]
0054DD51  mov     ecx, [ebp+arg_0]
0054DD54  mov     edx, [ebp+var_4]
0054DD57  push    1
0054DD59  push    0FFFFFFFFh
0054DD5B  push    1
0054DD5D  push    eax
0054DD5E  push    edi
0054DD5F  push    ebx
0054DD60  push    ecx
0054DD61  mov     ecx, [ebp+var_C]
0054DD64  call    SpawnMonster_3                    ; sub_6FC6A030
0054DD69  jmp     short loc_54DD82
0054DD6B  mov     edx, [ebp+arg_4]
0054DD6E  mov     ecx, [ebp+var_C]
0054DD71  push    1
0054DD73  push    0FFFFFFFFh
0054DD75  push    1
0054DD77  push    edx
0054DD78  mov     edx, [ebp+var_4]
0054DD7B  push    edi
0054DD7C  push    ebx
0054DD7D  call    SpawnMonster_1                    ; D2GAME_SpawnMonster_6FC69F10
0054DD82  test    eax, eax
0054DD84  jnz     short loc_54DDA3
0054DD86  mov     eax, [ebp+var_18]                 ; next attempt
0054DD89  add     eax, 1
0054DD8C  cmp     eax, 14h                          ; 20 attempts
0054DD8F  mov     [ebp+var_18], eax
0054DD92  jl      loc_54DC92
0054DD98  pop     edi
0054DD99  pop     esi
0054DD9A  xor     eax, eax
0054DD9C  pop     ebx
0054DD9D  mov     esp, ebp
0054DD9F  pop     ebp
0054DDA0  retn    14h
0054DDA3  mov     eax, [ebp+arg_8]
0054DDA6  mov     ecx, [ebp+arg_C]
0054DDA9  mov     [eax], ebx                        ; *pX = nX
0054DDAB  mov     [ecx], edi                        ; *pY = nY
0054DDAD  pop     edi
0054DDAE  pop     esi
0054DDAF  mov     eax, 1
0054DDB4  pop     ebx
0054DDB5  mov     esp, ebp
0054DDB7  pop     ebp
0054DDB8  retn    14h

Change

Rename the flag bNearWarp, place the monster only when it is clear, and drop the break after a near-spawn-point candidate so the loop tries the next one. The inner warp-coordinate loop still stops at the first warp that is close enough.

🤖 Generated with Claude Code

sub_6FC66260 picks up to 20 random positions to place a monster. MOO
set bSpawnMonster when a candidate was near a warp or the level's spawn
point and only tried to place the monster in that case, so:
- with the warp check off (a7 == 0) every candidate was rejected,
- with it on, only candidates next to a warp were accepted,
- a candidate near the spawn point broke out of the 20-attempt loop.

1.10f (D2Game.0x6FC66260): "a7 == 0" jumps straight to placement
(0x6FC663D3); a candidate near a warp (0x6FC6643E) or near the spawn
point (0x6FC664BE) jumps to the next attempt at 0x6FC66534; otherwise it
falls through to placement at 0x6FC664C0. 1.14d is the same
(Game.exe.0x54DC40, with the checks in CheckWarpSpawnCoords at
0x54DB50).

Rename the flag bNearWarp, place only when it is clear, and keep trying
after a near-spawn-point candidate.

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