KAZIK/src_graphics/billiards.inc
Игорь Федосеев ca62db53e9 feat: implement native billiards, scratch tickets, first-person dice, and city wake scene
- Billiards: add native Russian Pyramid & American Eight-Ball pool with physics prediction, controls, and regression tests
- Scratch Tickets: add dedicated counter, mat presentation, elliptical coin scraping, 4 ticket types, and payout effects
- First-Person Dice: add native dice scene, hand anchor frames, throwing animation, and table perspective
- City Wake & Weather: add arrival cutscene, alley geometry, weather/rain effects, and lighter/rocker animation
- World Dressing: add hall paintings, furniture packs, hall lighting, and scene regression tests
- Build & Tooling: update build.bat for modern MSVC toolchain, update launcher hash, and refresh shipping binaries
2026-10-16 12:37:40 +03:00

554 lines
12 KiB
PHP

; Q16.16 simulation, velocities in cloth pixels per 2 ms fixed step.
PoolSqrt3D PROC squared:DWORD
local result:DWORD
fild squared
fsqrt
fistp result
mov eax, result
ret
PoolSqrt3D ENDP
InitPool3D PROC
call ResetPool3D
mov poolState3D,POOL_SELECT
ret
InitPool3D ENDP
StartBilliards3D PROC variant:DWORD
cmp variant,1
ja @F
mov eax,variant
mov poolVariant3D,eax
call ResetPool3D
call ClearKeys3D
@@:
ret
StartBilliards3D ENDP
ResetPool3D PROC uses ebx esi edi
mov poolTraceValid3D,0
mov poolCapture2Q8,480*480
mov poolMiddleLeft3D,157*FP_1
mov poolMiddleRight3D,163*FP_1
lea esi,poolPyramidJawX3D
lea edi,poolJawX3D
mov ecx,24
rep movsd
cmp poolVariant3D,0
je @F
mov poolCapture2Q8,1024*1024
mov poolMiddleLeft3D,153*FP_1
mov poolMiddleRight3D,167*FP_1
lea esi,poolEightJawX3D
lea edi,poolJawX3D
mov ecx,24
rep movsd
@@:
lea edi, poolVX3D
xor eax, eax
mov ecx, 32
rep stosd
xor esi, esi
pool_init_balls:
mov poolActive3D[esi*4], 1
mov poolPocket3D[esi*4], -1
mov poolSinkTime3D[esi*4], 0
inc esi
cmp esi, 16
jb pool_init_balls
mov poolX3D, 82*FP_1
mov poolY3D, 115*FP_1
xor esi, esi
xor ebx, ebx
pool_rack_row:
xor edi, edi
pool_rack_ball:
mov ecx, poolRackOrder3D[esi*4]
imul eax, ebx, FP_1*43302/10000
add eax, 225*FP_1
mov poolX3D[ecx*4], eax
imul eax, edi, 5*FP_1
imul edx, ebx, FP_1*5/2
sub eax, edx
add eax, 115*FP_1
mov poolY3D[ecx*4], eax
inc esi
inc edi
cmp edi, ebx
jbe pool_rack_ball
inc ebx
cmp ebx, 5
jb pool_rack_row
mov poolState3D, POOL_AIM
mov poolPlayer3D, 0
mov poolGroups3D, 0
mov poolGroups3D[4], 0
mov poolAngle3D, 0
mov poolPower3D, 80000
mov poolBreak3D, 1
mov poolAccumulator3D, 0
mov poolCharge3D, 0
mov poolTime3D, 0
mov poolFoul3D, 0
mov poolPotted3D, 0
mov poolStill3D, 1
mov poolCalled3D, 1
mov poolCalledPocket3D, 0
mov poolCallManual3D,0
call InitPyramidState3D
ret
ResetPool3D ENDP
; Symmetric equal-mass impulse plus separation; opening contacts never gain energy.
PoolCollide3D PROC uses ebx esi edi a:DWORD,b:DWORD
local deltaX:DWORD,deltaY:DWORD,length8:DWORD,nx:DWORD,ny:DWORD,impulse:DWORD,correction:DWORD
mov esi, a
mov edi, b
cmp poolActive3D[esi*4], 0
je pool_pair_done
cmp poolActive3D[edi*4], 0
je pool_pair_done
mov eax, poolX3D[edi*4]
sub eax, poolX3D[esi*4]
cmp eax, POOL_DIAMETER
jg pool_pair_done
cmp eax, -POOL_DIAMETER
jl pool_pair_done
mov deltaX, eax
mov eax, poolY3D[edi*4]
sub eax, poolY3D[esi*4]
cmp eax, POOL_DIAMETER
jg pool_pair_done
cmp eax, -POOL_DIAMETER
jl pool_pair_done
mov deltaY, eax
sar eax, 8
imul eax, eax
mov ebx, deltaX
sar ebx, 8
imul ebx, ebx
add eax, ebx
cmp eax, 25*256*256
jg pool_pair_done
test eax, eax
jnz @F
mov deltaX, 256
mov eax, 1
@@:
invoke PoolSqrt3D, eax
cmp eax, 1
jge @F
mov eax, 1
@@:
mov length8, eax
mov eax, deltaX
imul eax, 256
cdq
idiv length8
mov nx, eax
mov eax, deltaY
imul eax, 256
cdq
idiv length8
mov ny, eax
mov eax, poolVX3D[edi*4]
sub eax, poolVX3D[esi*4]
imul nx
shrd eax, edx, 16
mov ebx, eax
mov eax, poolVY3D[edi*4]
sub eax, poolVY3D[esi*4]
imul ny
shrd eax, edx, 16
add eax, ebx
test eax, eax
jns pool_pair_separate
imul eax, 99
cdq
mov ecx, 100
idiv ecx
mov impulse, eax
imul nx
shrd eax, edx, 16
add poolVX3D[esi*4], eax
sub poolVX3D[edi*4], eax
mov eax, impulse
imul ny
shrd eax, edx, 16
add poolVY3D[esi*4], eax
sub poolVY3D[edi*4], eax
invoke PyramidContact3D,esi,edi
cmp esi,pyrCue3D
je @F
cmp edi,pyrCue3D
jne pool_pair_separate
cmp poolFirst3D,-1
jne pool_pair_separate
mov poolFirst3D,esi
jmp pool_pair_separate
@@:
cmp poolFirst3D, -1
jne pool_pair_separate
mov poolFirst3D, edi
pool_pair_separate:
mov eax, length8
shl eax, 8
neg eax
add eax, POOL_DIAMETER
sar eax, 1
mov correction, eax
imul nx
shrd eax, edx, 16
sub poolX3D[esi*4], eax
add poolX3D[edi*4], eax
mov eax, correction
imul ny
shrd eax, edx, 16
sub poolY3D[esi*4], eax
add poolY3D[edi*4], eax
pool_pair_done:
ret
PoolCollide3D ENDP
PoolPocketBall3D PROC uses ebx esi ball:DWORD
mov esi, ball
cmp poolActive3D[esi*4],0
je pool_pocket_done
xor ebx, ebx
pool_pocket_test:
mov eax, poolX3D[esi*4]
sub eax, poolPocketX3D[ebx*4]
cmp eax,5*FP_1
jg pool_pocket_next
cmp eax,-5*FP_1
jl pool_pocket_next
sar eax, 8
imul eax, eax
mov ecx, eax
mov eax, poolY3D[esi*4]
sub eax, poolPocketY3D[ebx*4]
cmp eax,5*FP_1
jg pool_pocket_next
cmp eax,-5*FP_1
jl pool_pocket_next
sar eax, 8
imul eax, eax
add eax, ecx
; Capture is deep inside the narrow mouth; jaws are separate colliders.
cmp eax, poolCapture2Q8
ja pool_pocket_next
pool_pocket_capture:
mov poolActive3D[esi*4], 0
mov poolVX3D[esi*4], 0
mov poolVY3D[esi*4], 0
mov poolPocket3D[esi*4], ebx
mov poolSinkTime3D[esi*4], 220
bts poolPotted3D, esi
ret
pool_pocket_next:
inc ebx
cmp ebx, 6
jb pool_pocket_test
pool_pocket_done:
ret
PoolPocketBall3D ENDP
PoolRailContact3D PROC ball:DWORD
mov ecx, ball
bts poolBreakRails3D, ecx
cmp poolFirst3D, -1
je @F
mov poolRail3D, 1
invoke PyramidRail3D,ball,pyrRailSide3D
@@:
ret
PoolRailContact3D ENDP
PoolJawContact3D PROC uses ebx esi edi ball:DWORD
local deltaX:DWORD,deltaY:DWORD,distance:DWORD,normX:DWORD,normY:DWORD,impulse:DWORD
mov esi, ball
xor edi, edi
pool_jaw_test:
mov eax, poolX3D[esi*4]
sub eax, poolJawX3D[edi*4]
cmp eax, 3*FP_1
jg pool_jaw_next
cmp eax, -3*FP_1
jl pool_jaw_next
mov deltaX, eax
mov eax, poolY3D[esi*4]
sub eax, poolJawY3D[edi*4]
cmp eax, 3*FP_1
jg pool_jaw_next
cmp eax, -3*FP_1
jl pool_jaw_next
mov deltaY, eax
sar eax, 8
imul eax, eax
mov ebx, deltaX
sar ebx, 8
imul ebx, ebx
add eax, ebx
cmp eax, 704*704
ja pool_jaw_next
test eax, eax
jnz @F
mov deltaX, 256
mov eax, 1
@@:
invoke PoolSqrt3D,eax
cmp eax, 1
jge @F
mov eax, 1
@@:
mov distance, eax
mov eax, deltaX
imul eax, 256
cdq
idiv distance
mov normX, eax
mov eax, deltaY
imul eax, 256
cdq
idiv distance
mov normY, eax
mov eax, poolVX3D[esi*4]
imul normX
shrd eax, edx, 16
mov ebx, eax
mov eax, poolVY3D[esi*4]
imul normY
shrd eax, edx, 16
add eax, ebx
test eax, eax
jns pool_jaw_separate
imul eax, 187
cdq
mov ecx, 100
idiv ecx
mov impulse, eax
imul normX
shrd eax, edx, 16
sub poolVX3D[esi*4], eax
mov eax, impulse
imul normY
shrd eax, edx, 16
sub poolVY3D[esi*4], eax
mov pyrRailSide3D,0
cmp edi,6
jb @F
mov pyrRailSide3D,1
@@:
cmp edi,1
je pyr_jaw_left_side
cmp edi,7
jne @F
pyr_jaw_left_side:
mov pyrRailSide3D,2
@@:
cmp edi,4
je pyr_jaw_right_side
cmp edi,10
jne @F
pyr_jaw_right_side:
mov pyrRailSide3D,3
@@:
invoke PoolRailContact3D,esi
pool_jaw_separate:
mov eax, distance
shl eax, 8
neg eax
add eax, FP_1*11/4
mov impulse, eax
imul normX
shrd eax, edx, 16
add poolX3D[esi*4], eax
mov eax, impulse
imul normY
shrd eax, edx, 16
add poolY3D[esi*4], eax
pool_jaw_next:
inc edi
cmp edi, 12
jb pool_jaw_test
ret
PoolJawContact3D ENDP
PoolStep3D PROC uses ebx esi edi
xor esi, esi
pool_step_ball:
cmp poolActive3D[esi*4], 0
je pool_step_next
mov eax,poolX3D[esi*4]
mov pyrPreviousX3D[esi*4],eax
mov eax, poolVX3D[esi*4]
add poolX3D[esi*4], eax
mov eax, poolVY3D[esi*4]
add poolY3D[esi*4], eax
invoke PyramidCross3D,esi
invoke PoolPocketBall3D, esi
cmp poolActive3D[esi*4], 0
je pool_step_next
invoke PoolJawContact3D,esi
; Leave the corner throats open. Solid cushions stop at their jaws.
mov ebx, 30*FP_1+POOL_RADIUS
mov edi, 290*FP_1-POOL_RADIUS
cmp poolY3D[esi*4], 57*FP_1
jle pool_rail_corner_x
cmp poolY3D[esi*4], 173*FP_1
jl pool_rail_test_x
pool_rail_corner_x:
mov ebx, 31*FP_1
mov edi, 289*FP_1
pool_rail_test_x:
cmp poolX3D[esi*4], ebx
jge pool_rail_right
mov poolX3D[esi*4], ebx
mov eax, poolVX3D[esi*4]
test eax, eax
jns pool_rail_y
jmp pool_rail_bounce_x
pool_rail_right:
cmp poolX3D[esi*4], edi
jle pool_rail_y
mov poolX3D[esi*4], edi
mov eax, poolVX3D[esi*4]
test eax, eax
jle pool_rail_y
pool_rail_bounce_x:
imul eax, -87
cdq
mov ecx, 100
idiv ecx
mov poolVX3D[esi*4], eax
mov pyrRailSide3D,2
cmp poolX3D[esi*4],160*FP_1
jl @F
mov pyrRailSide3D,3
@@:
invoke PoolRailContact3D, esi
pool_rail_y:
mov ebx, 50*FP_1+POOL_RADIUS
mov edi, 180*FP_1-POOL_RADIUS
cmp poolX3D[esi*4], 37*FP_1
jle pool_rail_throat_y
cmp poolX3D[esi*4], 283*FP_1
jge pool_rail_throat_y
mov eax,poolMiddleLeft3D
cmp poolX3D[esi*4], eax
jl pool_rail_test_y
mov eax,poolMiddleRight3D
cmp poolX3D[esi*4], eax
jg pool_rail_test_y
pool_rail_throat_y:
mov ebx, 51*FP_1
mov edi, 179*FP_1
pool_rail_test_y:
cmp poolY3D[esi*4], ebx
jge pool_rail_bottom
mov poolY3D[esi*4], ebx
mov eax, poolVY3D[esi*4]
test eax, eax
jns pool_step_next
jmp pool_rail_bounce_y
pool_rail_bottom:
cmp poolY3D[esi*4], edi
jle pool_step_next
mov poolY3D[esi*4], edi
mov eax, poolVY3D[esi*4]
test eax, eax
jle pool_step_next
pool_rail_bounce_y:
imul eax, -87
cdq
mov ecx, 100
idiv ecx
mov poolVY3D[esi*4], eax
mov pyrRailSide3D,0
cmp poolY3D[esi*4],115*FP_1
jl @F
mov pyrRailSide3D,1
@@:
invoke PoolRailContact3D, esi
pool_step_next:
inc esi
cmp esi, 16
jb pool_step_ball
; Two contact passes stabilize the triangular break rack.
mov ebx, 2
pool_contact_pass:
xor esi, esi
pool_contact_outer:
lea edi, [esi+1]
pool_contact_inner:
invoke PoolCollide3D, esi,edi
inc edi
cmp edi, 16
jb pool_contact_inner
inc esi
cmp esi, 15
jb pool_contact_outer
dec ebx
jnz pool_contact_pass
xor esi, esi
mov poolStill3D, 1
pool_friction_ball:
cmp poolActive3D[esi*4],0
je pool_sleep_ball
mov eax,poolVX3D[esi*4]
test eax,eax
jns @F
neg eax
@@:
add pyrRoll3D[esi*4],eax
mov eax,poolVY3D[esi*4]
test eax,eax
jns @F
neg eax
@@:
add pyrRoll3D[esi*4],eax
mov eax, poolVX3D[esi*4]
sar eax, 4
imul eax, eax
mov ecx, poolVY3D[esi*4]
sar ecx, 4
imul ecx, ecx
add eax, ecx
cmp eax, 64
jbe pool_sleep_ball
invoke PoolSqrt3D, eax
shl eax, 4
cmp eax, 128
jbe pool_sleep_ball
mov ecx, eax
sub eax, 16
xor edx, edx
shld edx, eax, 16
shl eax, 16
div ecx
mov ecx, eax
mov eax, poolVX3D[esi*4]
imul ecx
shrd eax, edx, 16
mov poolVX3D[esi*4], eax
mov eax, poolVY3D[esi*4]
imul ecx
shrd eax, edx, 16
mov poolVY3D[esi*4], eax
mov poolStill3D, 0
jmp pool_friction_next
pool_sleep_ball:
mov poolVX3D[esi*4], 0
mov poolVY3D[esi*4], 0
pool_friction_next:
inc esi
cmp esi, 16
jb pool_friction_ball
ret
PoolStep3D ENDP
include src_graphics\pyramid_rules.inc
include src_graphics\eightball.inc
include src_graphics\pyramid_play.inc
include src_graphics\billiards_prediction.inc
include src_graphics\pyramid_render.inc