1068 lines
23 KiB
PHP
1068 lines
23 KiB
PHP
; Native Backrooms: connected DFS maze, rooms/loops, BFS farthest exit.
|
|
LoadBackroomsAssets3D PROC uses esi edi
|
|
invoke LoadAnimPack3D,OFFSET brMaterials3D,OFFSET brMaterialPath3D
|
|
test eax,eax
|
|
jz br_assets_done
|
|
cmp brMaterials3D.imageWidth,128
|
|
jne br_assets_invalid
|
|
cmp brMaterials3D.height,128
|
|
jne br_assets_invalid
|
|
cmp brMaterials3D.frames,5
|
|
jne br_assets_invalid
|
|
mov esi,brMaterials3D.pixels
|
|
mov edi,OFFSET texturesBuffer+BR_WALL*65536
|
|
mov ecx,5*128*128
|
|
rep movsd
|
|
mov eax,1
|
|
ret
|
|
br_assets_invalid:
|
|
invoke FreeAnimPack3D,OFFSET brMaterials3D
|
|
xor eax,eax
|
|
br_assets_done:
|
|
ret
|
|
LoadBackroomsAssets3D ENDP
|
|
|
|
GenerateBackrooms3D PROC uses ebx esi edi
|
|
local top:DWORD,count:DWORD,current:DWORD,next:DWORD,tail:DWORD,head:DWORD
|
|
lea edi,brMap3D
|
|
mov eax,0B4B4B4B4h
|
|
mov ecx,256
|
|
rep stosd
|
|
mov brMap3D[33],0
|
|
mov brStack3D,33
|
|
mov top,1
|
|
br_dfs:
|
|
mov eax,top
|
|
test eax,eax
|
|
jz br_rooms
|
|
dec eax
|
|
mov ebx,brStack3D[eax*4]
|
|
mov current,ebx
|
|
mov count,0
|
|
xor esi,esi
|
|
br_neighbors:
|
|
mov edx,ebx
|
|
and edx,31
|
|
cmp esi,0
|
|
jne @F
|
|
cmp ebx,65
|
|
jb br_neighbor_next
|
|
@@:
|
|
cmp esi,1
|
|
jne @F
|
|
cmp edx,29
|
|
jae br_neighbor_next
|
|
@@:
|
|
cmp esi,2
|
|
jne @F
|
|
cmp ebx,29*32
|
|
jae br_neighbor_next
|
|
@@:
|
|
cmp esi,3
|
|
jne @F
|
|
cmp edx,3
|
|
jb br_neighbor_next
|
|
@@:
|
|
mov eax,ebx
|
|
add eax,brSteps3D[esi*4]
|
|
cmp brMap3D[eax],0
|
|
je br_neighbor_next
|
|
mov ecx,count
|
|
mov brCandidates3D[ecx*4],eax
|
|
inc count
|
|
br_neighbor_next:
|
|
inc esi
|
|
cmp esi,4
|
|
jb br_neighbors
|
|
cmp count,0
|
|
je br_backtrack
|
|
invoke RandomRange3D,count
|
|
mov eax,brCandidates3D[eax*4]
|
|
mov brMap3D[eax],0
|
|
mov edx,eax
|
|
add edx,current
|
|
shr edx,1
|
|
mov brMap3D[edx],0
|
|
mov ecx,top
|
|
mov brStack3D[ecx*4],eax
|
|
inc top
|
|
jmp br_dfs
|
|
br_backtrack:
|
|
dec top
|
|
jmp br_dfs
|
|
br_rooms:
|
|
; Open a small landing room so the recovered camera never faces a wall.
|
|
mov brMap3D[33],0
|
|
mov brMap3D[34],0
|
|
mov brMap3D[35],0
|
|
mov brMap3D[65],0
|
|
mov brMap3D[66],0
|
|
mov brMap3D[67],0
|
|
mov brMap3D[97],0
|
|
mov brMap3D[98],0
|
|
mov brMap3D[99],0
|
|
; Every 3x3 room includes an odd lattice node: cannot be disconnected.
|
|
mov esi,12
|
|
br_room:
|
|
invoke RandomRange3D,13
|
|
shl eax,1
|
|
inc eax
|
|
mov ebx,eax
|
|
invoke RandomRange3D,13
|
|
shl eax,1
|
|
inc eax
|
|
shl eax,5
|
|
add ebx,eax
|
|
mov edi,3
|
|
br_room_row:
|
|
mov brMap3D[ebx],0
|
|
mov brMap3D[ebx+1],0
|
|
mov brMap3D[ebx+2],0
|
|
add ebx,32
|
|
dec edi
|
|
jnz br_room_row
|
|
dec esi
|
|
jnz br_room
|
|
; Open connecting walls only; never carve outside the sealed border.
|
|
mov esi,55
|
|
br_loop:
|
|
invoke RandomRange3D,29
|
|
inc eax
|
|
mov ebx,eax
|
|
invoke RandomRange3D,29
|
|
inc eax
|
|
shl eax,5
|
|
add ebx,eax
|
|
cmp brMap3D[ebx-1],0
|
|
jne br_loop_vertical
|
|
cmp brMap3D[ebx+1],0
|
|
je br_loop_open
|
|
br_loop_vertical:
|
|
cmp brMap3D[ebx-32],0
|
|
jne br_loop_next
|
|
cmp brMap3D[ebx+32],0
|
|
jne br_loop_next
|
|
br_loop_open:
|
|
mov brMap3D[ebx],0
|
|
br_loop_next:
|
|
dec esi
|
|
jnz br_loop
|
|
lea edi,brDistance3D
|
|
mov eax,-1
|
|
mov ecx,1024
|
|
rep stosd
|
|
mov brDistance3D[33*4],0
|
|
mov brStack3D,33
|
|
mov head,0
|
|
mov tail,1
|
|
mov brExitCell3D,33
|
|
br_bfs:
|
|
mov eax,head
|
|
cmp eax,tail
|
|
jae br_place_games
|
|
mov ebx,brStack3D[eax*4]
|
|
inc head
|
|
mov eax,brDistance3D[ebx*4]
|
|
mov ecx,brExitCell3D
|
|
cmp eax,brDistance3D[ecx*4]
|
|
jbe @F
|
|
mov brExitCell3D,ebx
|
|
@@:
|
|
xor esi,esi
|
|
br_bfs_neighbor:
|
|
mov eax,brSteps3D[esi*4]
|
|
sar eax,1
|
|
add eax,ebx
|
|
cmp brMap3D[eax],0
|
|
jne br_bfs_next
|
|
cmp brDistance3D[eax*4],-1
|
|
jne br_bfs_next
|
|
mov edx,brDistance3D[ebx*4]
|
|
inc edx
|
|
mov brDistance3D[eax*4],edx
|
|
mov ecx,tail
|
|
mov brStack3D[ecx*4],eax
|
|
inc tail
|
|
br_bfs_next:
|
|
inc esi
|
|
cmp esi,4
|
|
jb br_bfs_neighbor
|
|
jmp br_bfs
|
|
br_place_games:
|
|
; Sample distinct reachable cells at evenly spaced BFS distances.
|
|
xor esi,esi
|
|
br_place_game:
|
|
mov eax,tail
|
|
imul eax,esi
|
|
xor edx,edx
|
|
mov ecx,7
|
|
div ecx
|
|
add eax,8
|
|
mov ebx,brStack3D[eax*4]
|
|
cmp ebx,brExitCell3D
|
|
jne @F
|
|
mov ebx,brStack3D[eax*4+4]
|
|
@@:
|
|
mov brGameCells3D[esi*4],ebx
|
|
inc esi
|
|
cmp esi,6
|
|
jb br_place_game
|
|
ret
|
|
GenerateBackrooms3D ENDP
|
|
|
|
BackroomsEntry3D PROC
|
|
cmp brActive3D,0
|
|
jne br_entry_reset
|
|
cmp engineMode,MODE_WALK
|
|
jne br_entry_reset
|
|
cmp hallLevel3D,0
|
|
jne br_entry_reset
|
|
cmp paused3D,0
|
|
jne br_entry_reset
|
|
cmp playerX,16*FP_1
|
|
jb br_entry_reset
|
|
cmp playerX,17*FP_1
|
|
jae br_entry_reset
|
|
cmp playerY,30*FP_1-15000
|
|
jb br_entry_reset
|
|
cmp playerY,30*FP_1
|
|
jae br_entry_reset
|
|
cmp playerAngle,104
|
|
jb br_entry_reset
|
|
cmp playerAngle,152
|
|
ja br_entry_reset
|
|
cmp keys3D[53h],0
|
|
jne br_entry_reset
|
|
cmp keys3D[28h],0
|
|
jne br_entry_reset
|
|
cmp keys3D[41h],0
|
|
jne br_entry_reset
|
|
cmp keys3D[44h],0
|
|
jne br_entry_reset
|
|
cmp keys3D[57h],0
|
|
jne br_entry_hold
|
|
cmp keys3D[26h],0
|
|
je br_entry_reset
|
|
br_entry_hold:
|
|
mov eax,playerAngle
|
|
cmp brEntryHold3D,0
|
|
je @F
|
|
cmp eax,brEntryAngle3D
|
|
jne br_entry_reset
|
|
@@:
|
|
mov brEntryAngle3D,eax
|
|
mov eax,deltaTime3D
|
|
add brEntryHold3D,eax
|
|
cmp brEntryHold3D,2000
|
|
jb br_entry_done
|
|
call BeginBackrooms3D
|
|
ret
|
|
br_entry_reset:
|
|
mov brEntryHold3D,0
|
|
br_entry_done:
|
|
ret
|
|
BackroomsEntry3D ENDP
|
|
|
|
BeginBackrooms3D PROC
|
|
call LoadBackroomsAssets3D
|
|
test eax,eax
|
|
jz br_begin_done
|
|
call GenerateBackrooms3D
|
|
mov brActive3D,1
|
|
mov brEntryHold3D,0
|
|
mov brCutscene3D,1
|
|
mov brClock3D,0
|
|
mov brDialogue3D,0
|
|
mov paused3D,0
|
|
; First 900ms still show the casino while camera clips through the wall.
|
|
mov viewEye3D,HALL_STANDING_EYE
|
|
mov viewCeiling3D,FP_1*5/2
|
|
mov interactTargetId,0
|
|
call ClearKeys3D
|
|
br_begin_done:
|
|
ret
|
|
BeginBackrooms3D ENDP
|
|
|
|
LeaveBackrooms3D PROC reward:DWORD
|
|
cmp brActive3D,0
|
|
je br_leave_done
|
|
cmp reward,0
|
|
je @F
|
|
cmp brOwned3D,1
|
|
je @F
|
|
mov brOwned3D,1
|
|
mov brEnabled3D,1
|
|
@@:
|
|
invoke CopyString3D,OFFSET brAward3D,OFFSET flashMessage
|
|
mov flashTimer,200
|
|
@@:
|
|
mov brActive3D,0
|
|
mov brCutscene3D,0
|
|
mov brEntryHold3D,0
|
|
mov worldMap3D,OFFSET casinoMap
|
|
mov hallLevel3D,0
|
|
mov playerX,16*FP_1+FP_HALF
|
|
mov playerY,27*FP_1+FP_HALF
|
|
mov playerAngle,384
|
|
mov viewEye3D,HALL_STANDING_EYE
|
|
mov viewCeiling3D,FP_1*5/2
|
|
mov floorTexture3D,100
|
|
mov engineMode,MODE_WALK
|
|
mov paused3D,0
|
|
call ClearKeys3D
|
|
call SaveProgress3D
|
|
br_leave_done:
|
|
ret
|
|
LeaveBackrooms3D ENDP
|
|
|
|
HandleBackrooms3D PROC uses ebx esi edi
|
|
mov eax,deltaTime3D
|
|
add brClock3D,eax
|
|
cmp brCutscene3D,0
|
|
je br_walk
|
|
cmp brClock3D,900
|
|
jae br_fall_world
|
|
mov eax,deltaTime3D
|
|
imul eax,30
|
|
add playerY,eax
|
|
ret
|
|
br_fall_world:
|
|
cmp hallLevel3D,HALL_BACKROOMS
|
|
je @F
|
|
mov hallLevel3D,HALL_BACKROOMS
|
|
mov worldMap3D,OFFSET brMap3D
|
|
mov playerX,FP_1+FP_HALF
|
|
mov playerY,FP_1+FP_HALF
|
|
mov playerAngle,64
|
|
mov floorTexture3D,BR_FLOOR
|
|
@@:
|
|
cmp brClock3D,6500
|
|
jae br_stand
|
|
; Camera fall, impact, then low-to-standing recovery; no video player.
|
|
mov eax,brClock3D
|
|
cmp eax,2200
|
|
jae br_impact
|
|
imul eax,36
|
|
mov edx,FP_1*3/2
|
|
sub edx,eax
|
|
cmp edx,FP_1/8
|
|
jge @F
|
|
mov edx,FP_1/8
|
|
@@:
|
|
mov viewEye3D,edx
|
|
mov eax,brClock3D
|
|
shr eax,5
|
|
and eax,511
|
|
mov playerAngle,eax
|
|
ret
|
|
br_impact:
|
|
cmp eax,3100
|
|
jae br_rise
|
|
mov viewEye3D,FP_1/8
|
|
mov playerAngle,64
|
|
ret
|
|
br_rise:
|
|
sub eax,3100
|
|
imul eax,HALL_STANDING_EYE-FP_1/8
|
|
xor edx,edx
|
|
mov ecx,3400
|
|
div ecx
|
|
add eax,FP_1/8
|
|
mov viewEye3D,eax
|
|
ret
|
|
br_stand:
|
|
mov brCutscene3D,0
|
|
mov viewEye3D,HALL_STANDING_EYE
|
|
call ClearKeys3D
|
|
br_walk:
|
|
mov eax,deltaTime3D
|
|
add brDialogue3D,eax
|
|
call HandlePlayerInput
|
|
mov interactTargetId,0
|
|
mov interactPromptPtr,0
|
|
mov brTarget3D,-1
|
|
xor esi,esi
|
|
br_target_scan:
|
|
cmp esi,6
|
|
jb @F
|
|
mov ebx,brExitCell3D
|
|
jmp br_target_cell
|
|
@@:
|
|
mov ebx,brGameCells3D[esi*4]
|
|
br_target_cell:
|
|
mov eax,ebx
|
|
and eax,31
|
|
shl eax,16
|
|
add eax,FP_HALF
|
|
sub eax,playerX
|
|
sar eax,8
|
|
imul eax,eax
|
|
mov edi,eax
|
|
mov eax,ebx
|
|
shr eax,5
|
|
shl eax,16
|
|
add eax,FP_HALF
|
|
sub eax,playerY
|
|
sar eax,8
|
|
imul eax,eax
|
|
add eax,edi
|
|
cmp eax,FP_1*3/4
|
|
ja br_target_next
|
|
mov brTarget3D,esi
|
|
cmp esi,6
|
|
je @F
|
|
mov eax,brGamePrompts3D[esi*4]
|
|
mov interactPromptPtr,eax
|
|
jmp br_targets_done
|
|
@@:
|
|
mov interactPromptPtr,OFFSET brExitPrompt3D
|
|
jmp br_targets_done
|
|
br_target_next:
|
|
inc esi
|
|
cmp esi,7
|
|
jb br_target_scan
|
|
br_targets_done:
|
|
invoke Pressed3D,56h
|
|
test eax,eax
|
|
jz @F
|
|
xor brVhs3D,1
|
|
@@:
|
|
invoke Pressed3D,45h
|
|
test eax,eax
|
|
jz br_handle_done
|
|
mov esi,brTarget3D
|
|
cmp esi,6
|
|
ja br_handle_done
|
|
jne br_launch
|
|
invoke LeaveBackrooms3D,1
|
|
ret
|
|
br_launch:
|
|
; Reuse normal station dispatch; suppress all hall-specific interactions.
|
|
mov eax,brGames3D[esi*4]
|
|
mov interactTargetId,eax
|
|
call Trigger3DInteraction
|
|
call ClearKeys3D
|
|
br_handle_done:
|
|
ret
|
|
HandleBackrooms3D ENDP
|
|
|
|
RenderBackroomsDialogue3D PROC uses ebx esi edi
|
|
local x:DWORD,y:DWORD,age:DWORD,offsetY:DWORD
|
|
cmp brDialogue3D,9500
|
|
jae br_dialogue_done
|
|
invoke DrawBoxBorder,4,179,312,57,00191711h,009C9464h,1
|
|
mov x,12
|
|
mov y,189
|
|
xor esi,esi
|
|
br_letter:
|
|
movzx ebx,brText3D[esi]
|
|
test ebx,ebx
|
|
jz br_dialogue_done
|
|
; Wrap whole words, not arbitrary characters.
|
|
cmp ebx,32
|
|
jne br_letter_position
|
|
mov edi,esi
|
|
xor ecx,ecx
|
|
br_word_length:
|
|
inc edi
|
|
mov al,brText3D[edi]
|
|
test al,al
|
|
jz br_word_end
|
|
cmp al,32
|
|
je br_word_end
|
|
inc ecx
|
|
jmp br_word_length
|
|
br_word_end:
|
|
inc ecx
|
|
shl ecx,3
|
|
add ecx,x
|
|
cmp ecx,300
|
|
jbe br_letter_position
|
|
mov x,12
|
|
add y,14
|
|
jmp br_letter_next
|
|
br_letter_position:
|
|
mov eax,esi
|
|
imul eax,35
|
|
mov ecx,brDialogue3D
|
|
sub ecx,eax
|
|
js br_letter_advance
|
|
cmp ecx,6400
|
|
jae br_letter_advance
|
|
mov age,ecx
|
|
mov offsetY,0
|
|
cmp ecx,420
|
|
jae br_letter_out
|
|
mov eax,420
|
|
sub eax,ecx
|
|
imul eax,18
|
|
xor edx,edx
|
|
mov ecx,420
|
|
div ecx
|
|
mov offsetY,eax
|
|
jmp br_letter_draw
|
|
br_letter_out:
|
|
cmp ecx,5900
|
|
jb br_letter_draw
|
|
sub ecx,5900
|
|
imul ecx,18
|
|
mov eax,ecx
|
|
xor edx,edx
|
|
mov ecx,500
|
|
div ecx
|
|
neg eax
|
|
mov offsetY,eax
|
|
br_letter_draw:
|
|
mov eax,age
|
|
shr eax,3
|
|
mov ecx,esi
|
|
shl ecx,4
|
|
add eax,ecx
|
|
and eax,511
|
|
mov eax,tableSin[eax*4]
|
|
sar eax,15
|
|
add eax,y
|
|
add eax,offsetY
|
|
mov brGlyph3D,bl
|
|
invoke DrawString8x8,x,eax,OFFSET brGlyph3D,00EBE4B5h
|
|
br_letter_advance:
|
|
add x,8
|
|
br_letter_next:
|
|
inc esi
|
|
jmp br_letter
|
|
br_dialogue_done:
|
|
ret
|
|
RenderBackroomsDialogue3D ENDP
|
|
|
|
RenderBackrooms3D PROC uses ebx esi edi
|
|
local exitX:DWORD,exitY:DWORD
|
|
call UpdateCamera3D
|
|
call RenderCeilingAndFloor
|
|
call RenderWalls
|
|
call PrepareCityDepth3D
|
|
cmp hallLevel3D,HALL_BACKROOMS
|
|
je @F
|
|
call RenderSprites
|
|
call RenderHall3D
|
|
jmp br_camera_hud
|
|
@@:
|
|
xor esi,esi
|
|
br_render_station:
|
|
mov ebx,brGameCells3D[esi*4]
|
|
mov eax,ebx
|
|
and eax,31
|
|
shl eax,16
|
|
add eax,FP_HALF
|
|
mov brProp3D.posX,eax
|
|
mov eax,ebx
|
|
shr eax,5
|
|
shl eax,16
|
|
add eax,FP_HALF
|
|
mov brProp3D.posY,eax
|
|
mov eax,brGamePacks3D[esi*4]
|
|
mov brProp3D.asset,eax
|
|
mov eax,brGameFrames3D[esi*4]
|
|
mov brProp3D.frame,eax
|
|
invoke DrawCityObject3D,OFFSET brProp3D,0
|
|
inc esi
|
|
cmp esi,6
|
|
jb br_render_station
|
|
; Upright door on a neighboring solid wall; collision map stays unchanged.
|
|
mov ebx,brExitCell3D
|
|
mov eax,ebx
|
|
and eax,31
|
|
shl eax,16
|
|
mov exitX,eax
|
|
mov eax,ebx
|
|
shr eax,5
|
|
shl eax,16
|
|
mov exitY,eax
|
|
cmp brMap3D[ebx-32],0
|
|
je br_exit_south
|
|
add exitY,512
|
|
jmp br_exit_y_door
|
|
br_exit_south:
|
|
cmp brMap3D[ebx+32],0
|
|
je br_exit_west
|
|
add exitY,FP_1-512
|
|
br_exit_y_door:
|
|
mov eax,exitX
|
|
add eax,FP_1
|
|
invoke DrawCityVertical3D,1,exitY,exitX,eax,0,FP_1*21/10,BR_EXIT
|
|
jmp br_exit_drawn
|
|
br_exit_west:
|
|
cmp brMap3D[ebx-1],0
|
|
je br_exit_east
|
|
add exitX,512
|
|
jmp br_exit_x_door
|
|
br_exit_east:
|
|
add exitX,FP_1-512
|
|
br_exit_x_door:
|
|
mov eax,exitY
|
|
add eax,FP_1
|
|
invoke DrawCityVertical3D,0,exitX,exitY,eax,0,FP_1*21/10,BR_EXIT
|
|
br_exit_drawn:
|
|
cmp brVhs3D,0
|
|
je br_camera_hud
|
|
; Deterministic screen noise, independent of gameplay RNG.
|
|
lea edi,screenBuffer
|
|
mov ecx,SCREEN_SIZE
|
|
mov ebx,brClock3D
|
|
br_vhs_pixel:
|
|
imul ebx,1664525
|
|
add ebx,1013904223
|
|
mov eax,[edi]
|
|
; Reserve two low bits per channel: adding 0..3 cannot carry into RGB.
|
|
and eax,00FCFCFCh
|
|
mov edx,ebx
|
|
shr edx,30
|
|
imul edx,00010101h
|
|
add eax,edx
|
|
stosd
|
|
dec ecx
|
|
jnz br_vhs_pixel
|
|
mov esi,2
|
|
br_scanline:
|
|
imul eax,esi,SCREEN_W*4
|
|
lea edi,screenBuffer[eax]
|
|
mov ecx,SCREEN_W
|
|
br_scan_pixel:
|
|
mov eax,[edi]
|
|
and eax,00FCFCFCh
|
|
mov edx,eax
|
|
shr edx,2
|
|
sub eax,edx
|
|
stosd
|
|
loop br_scan_pixel
|
|
add esi,6
|
|
cmp esi,SCREEN_H
|
|
jb br_scanline
|
|
br_camera_hud:
|
|
mov eax,brClock3D
|
|
xor edx,edx
|
|
mov ecx,1000
|
|
div ecx
|
|
xor esi,esi
|
|
mov esi,6
|
|
br_time_pair:
|
|
xor edx,edx
|
|
mov ecx,60
|
|
div ecx
|
|
push eax
|
|
mov eax,edx
|
|
xor edx,edx
|
|
mov ecx,10
|
|
div ecx
|
|
add al,'0'
|
|
add dl,'0'
|
|
mov brTime3D[esi],al
|
|
mov brTime3D[esi+1],dl
|
|
pop eax
|
|
sub esi,3
|
|
jns br_time_pair
|
|
invoke DrawString8x8,224,12,OFFSET brTime3D,00EEEEDDh
|
|
invoke DrawString8x8,25,12,OFFSET brRec3D,00EEEEDDh
|
|
test brClock3D,512
|
|
jnz @F
|
|
invoke DrawBox,12,13,7,7,00EE3030h
|
|
@@:
|
|
invoke DrawString8x8,8,29,OFFSET brHelp3D,00C8C8A0h
|
|
cmp brCutscene3D,0
|
|
je br_hud_walk
|
|
cmp brClock3D,2200
|
|
jb br_render_done
|
|
cmp brClock3D,2800
|
|
jae br_render_done
|
|
invoke DrawBox,0,0,SCREEN_W,SCREEN_H,00080704h
|
|
ret
|
|
br_hud_walk:
|
|
cmp interactPromptPtr,0
|
|
je @F
|
|
invoke DrawString8x8,112,158,interactPromptPtr,00F1F1B5h
|
|
@@:
|
|
call RenderBackroomsDialogue3D
|
|
br_render_done:
|
|
ret
|
|
RenderBackrooms3D ENDP
|
|
|
|
; -1 = disabled; 0 = unlucky; 1 = lucky. Caller latches once per round.
|
|
BackroomsLuck3D PROC
|
|
cmp brOwned3D,1
|
|
jne br_luck_off
|
|
cmp brEnabled3D,1
|
|
jne br_luck_off
|
|
cmp brActive3D,0
|
|
jne br_luck_off
|
|
cmp tournamentMode,0
|
|
jne br_luck_off
|
|
cmp obsActive3D,0
|
|
jne br_luck_off
|
|
invoke RandomRange3D,100
|
|
cmp eax,90
|
|
setb al
|
|
movzx eax,al
|
|
ret
|
|
br_luck_off:
|
|
mov eax,-1
|
|
ret
|
|
BackroomsLuck3D ENDP
|
|
|
|
; Select a physically possible roulette result by actual net return.
|
|
BackroomsWheelLuck3D PROC uses ebx esi edi original:DWORD
|
|
local desired:DWORD,total:DWORD,count:DWORD
|
|
call BackroomsLuck3D
|
|
cmp eax,-1
|
|
je br_wheel_original
|
|
mov desired,eax
|
|
mov count,0
|
|
xor edi,edi
|
|
br_wheel_result:
|
|
mov total,0
|
|
xor esi,esi
|
|
br_wheel_bet:
|
|
invoke WheelBetMultiplier3D,esi,edi
|
|
imul eax,wheelBets3D[esi*4]
|
|
add total,eax
|
|
inc esi
|
|
cmp esi,49
|
|
jb br_wheel_bet
|
|
mov eax,total
|
|
cmp eax,wheelStake3D
|
|
seta al
|
|
movzx eax,al
|
|
cmp eax,desired
|
|
jne br_wheel_next
|
|
inc count
|
|
invoke RandomRange3D,count
|
|
test eax,eax
|
|
jnz br_wheel_next
|
|
mov original,edi
|
|
br_wheel_next:
|
|
inc edi
|
|
cmp edi,37
|
|
jb br_wheel_result
|
|
br_wheel_original:
|
|
mov eax,original
|
|
ret
|
|
BackroomsWheelLuck3D ENDP
|
|
|
|
; Permute a real deck by swapping cards, preserving all 52 unique identities.
|
|
BackroomsDeck3D PROC uses ebx esi edi deck:DWORD,targets:DWORD,count:DWORD
|
|
mov edi,deck
|
|
mov esi,targets
|
|
xor ebx,ebx
|
|
br_deck_position:
|
|
cmp ebx,count
|
|
jae br_deck_done
|
|
mov eax,[esi+ebx*4]
|
|
cmp eax,52
|
|
jae br_deck_next
|
|
xor ecx,ecx
|
|
br_deck_find:
|
|
cmp ecx,52
|
|
jae br_deck_next
|
|
cmp [edi+ecx*4],eax
|
|
je br_deck_swap
|
|
inc ecx
|
|
jmp br_deck_find
|
|
br_deck_swap:
|
|
mov edx,[edi+ebx*4]
|
|
mov [edi+ecx*4],edx
|
|
mov [edi+ebx*4],eax
|
|
br_deck_next:
|
|
inc ebx
|
|
jmp br_deck_position
|
|
br_deck_done:
|
|
ret
|
|
BackroomsDeck3D ENDP
|
|
|
|
BackroomsBlackjackDeck3D PROC uses ebx esi edi
|
|
call BackroomsLuck3D
|
|
cmp eax,-1
|
|
je br_bj_done
|
|
mov ebx,eax
|
|
lea edi,brDeckTargets3D
|
|
mov eax,-1
|
|
mov ecx,16
|
|
rep stosd
|
|
; Two aces + picture cards remain distinct. Party seats keep their cards.
|
|
mov ecx,cbBotCount3D
|
|
add ecx,2
|
|
cmp ebx,1
|
|
jne br_bj_unlucky
|
|
mov brDeckTargets3D,12
|
|
mov brDeckTargets3D[ecx*4],11
|
|
dec ecx
|
|
mov brDeckTargets3D[ecx*4],8
|
|
inc ecx
|
|
add ecx,ecx
|
|
dec ecx
|
|
mov brDeckTargets3D[ecx*4],7
|
|
jmp br_bj_apply
|
|
br_bj_unlucky:
|
|
mov brDeckTargets3D,8
|
|
mov brDeckTargets3D[ecx*4],7
|
|
dec ecx
|
|
mov brDeckTargets3D[ecx*4],12
|
|
inc ecx
|
|
add ecx,ecx
|
|
dec ecx
|
|
mov brDeckTargets3D[ecx*4],11
|
|
br_bj_apply:
|
|
invoke BackroomsDeck3D,OFFSET cbDeck3D,OFFSET brDeckTargets3D,10
|
|
br_bj_done:
|
|
ret
|
|
BackroomsBlackjackDeck3D ENDP
|
|
|
|
BackroomsPokerDeck3D PROC uses ebx edi
|
|
call BackroomsLuck3D
|
|
cmp eax,-1
|
|
je br_pk_done
|
|
mov ebx,eax
|
|
lea edi,brDeckTargets3D
|
|
mov eax,-1
|
|
mov ecx,16
|
|
rep stosd
|
|
; Fixed board creates a real royal flush for the lucky seat, no duplicate cards.
|
|
; Flop deck 5,6,7; turn 9; river 11. Burns remain shuffled.
|
|
mov brDeckTargets3D[5*4],8
|
|
mov brDeckTargets3D[6*4],9
|
|
mov brDeckTargets3D[7*4],10
|
|
mov brDeckTargets3D[9*4],13
|
|
mov brDeckTargets3D[11*4],27
|
|
mov ecx,pkButton3D
|
|
xor ecx,1
|
|
xor ebx,1
|
|
xor ecx,ebx
|
|
mov brDeckTargets3D[ecx*4],11
|
|
mov brDeckTargets3D[ecx*4+8],12
|
|
xor ecx,1
|
|
mov brDeckTargets3D[ecx*4],16
|
|
mov brDeckTargets3D[ecx*4+8],30
|
|
invoke BackroomsDeck3D,OFFSET pkDeck3D,OFFSET brDeckTargets3D,12
|
|
br_pk_done:
|
|
ret
|
|
BackroomsPokerDeck3D ENDP
|
|
|
|
BackroomsDurakDeck3D PROC uses ebx esi edi
|
|
call BackroomsLuck3D
|
|
cmp eax,-1
|
|
je br_dk_done
|
|
mov ebx,eax
|
|
; Favor six initial trump cards, preserving the compacted 36-card deck.
|
|
; A poor move can still lose: no settlement or rule is overridden.
|
|
mov ecx,0
|
|
br_dk_pair:
|
|
mov eax,ecx
|
|
add eax,7
|
|
cmp ebx,1
|
|
jne @F
|
|
add eax,26
|
|
@@:
|
|
mov edx,ecx
|
|
shl edx,1
|
|
xor esi,esi
|
|
br_dk_find:
|
|
cmp esi,36
|
|
jae br_dk_next
|
|
cmp dkDeck3D[esi*4],eax
|
|
je br_dk_swap
|
|
inc esi
|
|
jmp br_dk_find
|
|
br_dk_swap:
|
|
mov edi,dkDeck3D[edx*4]
|
|
mov dkDeck3D[esi*4],edi
|
|
mov dkDeck3D[edx*4],eax
|
|
br_dk_next:
|
|
inc ecx
|
|
cmp ecx,6
|
|
jb br_dk_pair
|
|
; Exposed trump must match the favored side, without introducing duplicates.
|
|
mov eax,43
|
|
cmp ebx,1
|
|
jne @F
|
|
mov eax,30
|
|
@@:
|
|
xor esi,esi
|
|
br_dk_trump_find:
|
|
cmp dkDeck3D[esi*4],eax
|
|
je br_dk_trump_swap
|
|
inc esi
|
|
cmp esi,36
|
|
jb br_dk_trump_find
|
|
jmp br_dk_done
|
|
br_dk_trump_swap:
|
|
mov edx,dkDeck3D[35*4]
|
|
mov dkDeck3D[esi*4],edx
|
|
mov dkDeck3D[35*4],eax
|
|
br_dk_done:
|
|
ret
|
|
BackroomsDurakDeck3D ENDP
|
|
|
|
; Reorder only at loading time, before shell knowledge or previews exist.
|
|
; This biases real cartridges, never changes a cartridge after it is revealed.
|
|
BackroomsBuckshotLuck3D PROC uses ebx esi edi
|
|
call BackroomsLuck3D
|
|
cmp eax,-1
|
|
je br_bs_done
|
|
mov brBuckshotLuck3D,eax
|
|
xor eax,bs3d_turn
|
|
mov ebx,eax
|
|
xor esi,esi
|
|
br_bs_shell:
|
|
; Only permute cartridges already loaded; preserve advertised counts.
|
|
xor eax,eax
|
|
cmp ebx,1
|
|
jne br_bs_blanks_first
|
|
cmp esi,bs3d_liveCount
|
|
setb al
|
|
jmp br_bs_store
|
|
br_bs_blanks_first:
|
|
cmp esi,bs3d_blankCount
|
|
setae al
|
|
br_bs_store:
|
|
mov bs3d_shells[esi*4],eax
|
|
inc esi
|
|
cmp esi,bs3d_shellCount
|
|
jb br_bs_shell
|
|
br_bs_done:
|
|
ret
|
|
BackroomsBuckshotLuck3D ENDP
|
|
|
|
BackroomsRussianLuck3D PROC
|
|
; Cylinder is arranged before LOAD/SPIN; shot animation consumes it unchanged.
|
|
call BackroomsLuck3D
|
|
cmp eax,-1
|
|
je br_rr_done
|
|
mov brRussianLuck3D,eax
|
|
mov rr3d_turn,eax
|
|
call BackroomsRussianCylinder3D
|
|
br_rr_done:
|
|
ret
|
|
BackroomsRussianLuck3D ENDP
|
|
|
|
; Search legal cylinders before loading animation, not at the trigger frame.
|
|
; Keep selected bullet count. Lucky cylinders eliminate guests, never player;
|
|
; subsequent reloads retain the single round's luck roll.
|
|
BackroomsRussianCylinder3D PROC uses ebx esi edi
|
|
local candidate:DWORD,alive:DWORD,actor:DWORD,step:DWORD
|
|
cmp brRussianLuck3D,-1
|
|
je br_cylinder_done
|
|
cmp brRussianLuck3D,0
|
|
jne br_cylinder_search
|
|
mov eax,1
|
|
mov ecx,rr3d_bullets
|
|
shl eax,cl
|
|
dec eax
|
|
mov rr3d_mask,eax
|
|
mov rr3d_turn,0
|
|
mov rr3d_chamber,0
|
|
ret
|
|
br_cylinder_search:
|
|
mov candidate,1
|
|
br_cylinder_candidate:
|
|
mov eax,candidate
|
|
xor ecx,ecx
|
|
br_cylinder_popcount:
|
|
shr eax,1
|
|
adc ecx,0
|
|
test eax,eax
|
|
jnz br_cylinder_popcount
|
|
cmp ecx,rr3d_bullets
|
|
jne br_cylinder_next
|
|
mov eax,rrPartyAlive3D
|
|
mov alive,eax
|
|
mov eax,rr3d_turn
|
|
mov actor,eax
|
|
mov step,0
|
|
br_cylinder_simulate:
|
|
mov eax,candidate
|
|
mov ecx,step
|
|
bt eax,ecx
|
|
jnc br_cylinder_advance
|
|
mov ecx,actor
|
|
test ecx,ecx
|
|
jz br_cylinder_next
|
|
btr alive,ecx
|
|
cmp alive,1
|
|
je br_cylinder_found
|
|
br_cylinder_advance:
|
|
inc actor
|
|
mov eax,actor
|
|
cmp eax,rrPartySize3D
|
|
jb @F
|
|
mov actor,0
|
|
xor eax,eax
|
|
@@:
|
|
bt alive,eax
|
|
jnc br_cylinder_advance
|
|
inc step
|
|
cmp step,6
|
|
jb br_cylinder_simulate
|
|
br_cylinder_found:
|
|
mov eax,candidate
|
|
mov rr3d_mask,eax
|
|
mov rr3d_chamber,0
|
|
ret
|
|
br_cylinder_next:
|
|
inc candidate
|
|
cmp candidate,64
|
|
jb br_cylinder_candidate
|
|
br_cylinder_done:
|
|
ret
|
|
BackroomsRussianCylinder3D ENDP
|
|
|
|
HandleBackroomsInventory3D PROC
|
|
cmp brOwned3D,0
|
|
jne @F
|
|
mov brInventoryOpen3D,0
|
|
xor eax,eax
|
|
ret
|
|
@@:
|
|
invoke Pressed3D,52h
|
|
test eax,eax
|
|
jz @F
|
|
xor brInventoryOpen3D,1
|
|
mov eax,1
|
|
ret
|
|
@@:
|
|
cmp brInventoryOpen3D,0
|
|
je br_inventory_unhandled
|
|
invoke Pressed3D,09h
|
|
test eax,eax
|
|
jz @F
|
|
mov brInventoryOpen3D,0
|
|
@@:
|
|
invoke Pressed3D,45h
|
|
test eax,eax
|
|
jz @F
|
|
xor brEnabled3D,1
|
|
call SaveProgress3D
|
|
@@:
|
|
mov eax,1
|
|
ret
|
|
br_inventory_unhandled:
|
|
xor eax,eax
|
|
ret
|
|
HandleBackroomsInventory3D ENDP
|
|
|
|
RenderBackroomsInventory3D PROC uses esi
|
|
invoke DrawBox,0,0,320,240,0015191Bh
|
|
invoke DrawBoxBorder,8,8,304,224,001D2425h,00D8BC6Ah,1
|
|
invoke DrawString8x8,24,24,OFFSET brInventory3D,00EED5A0h
|
|
invoke DrawString8x8,24,52,OFFSET brInventoryRules1,00FFFFFFh
|
|
invoke DrawString8x8,24,72,OFFSET brInventoryRules2,00FFFFFFh
|
|
invoke DrawString8x8,24,100,OFFSET brInventoryRules3,00DDCDAAh
|
|
invoke DrawString8x8,24,120,OFFSET brInventoryRules4,00DDCDAAh
|
|
invoke DrawString8x8,24,140,OFFSET brInventoryRules5,00DDCDAAh
|
|
invoke DrawString8x8,24,160,OFFSET brInventoryRules6,00DDCDAAh
|
|
invoke DrawString8x8,24,188,OFFSET brInventoryHelp3D,00D8BC6Ah
|
|
mov esi,OFFSET brDisabledText3D
|
|
cmp brEnabled3D,0
|
|
je @F
|
|
mov esi,OFFSET brEnabledText3D
|
|
@@:
|
|
invoke DrawString8x8,264,188,esi,00FFFFFFh
|
|
invoke DrawString8x8,24,212,OFFSET brInventoryReturn3D,0099AAA5h
|
|
ret
|
|
RenderBackroomsInventory3D ENDP
|