KAZIK/tools/tests/bowling_feel_test.inc
Artvasil09 7e081beeca feat: enhance bowling physics, audio, ball weight selection, and visitor cast routines
- Bowling feel: impulse contact solver, string pinsetter cycle, in-world lengthwise return rack and visible ball return lift

- Ball weights: 4/5/6/7 kg selection with equal launch work physics, momentum balance, and projected picking

- Audio: dedicated MCI channels with 7 synthesized waveaudio cues (release, rolling, impacts, applause, pinsetter, return)

- First-person animations: 33-frame player underarm throw/follow-through sequence (player-underarm-v2)

- Visitors: 10 persistent shared cast identities with exclusive reservations, activity transitions, and chair sit/rise animations

- Tests & verification: comprehensive native regression test suites and updated documentation

- Synchronize binaries and update launcher hash to 536E23BED4055562BC63871826FF44FFED037923A9BE8E0F591131DB4FBD104B
2026-11-04 18:44:12 +03:00

455 lines
12 KiB
PHP

.data
bpFeelScoreRolls dd 10,7,3,0,8,5,4,9,0,6,2,8,1,7,2,9,0,10,7,3
bpFeelScorePath db 'build\hall-expansion\bowling-scorecard-full.bmp',0
bpFeelPinPath0 db 'build\hall-expansion\bowling-impact-0.bmp',0
bpFeelPinPath1 db 'build\hall-expansion\bowling-impact-1.bmp',0
bpFeelPinPath2 db 'build\hall-expansion\bowling-impact-2.bmp',0
bpFeelPinPath3 db 'build\hall-expansion\bowling-impact-3.bmp',0
bpFeelPinPaths dd OFFSET bpFeelPinPath0,OFFSET bpFeelPinPath1,OFFSET bpFeelPinPath2,OFFSET bpFeelPinPath3
bpFeelStatus db 'status bowlroll mode',0
bpFeelStatusLight db 'status bowllight mode',0
bpFeelStatusReset db 'status bowlreset mode',0
bpFeelStatusReturn db 'status bowlreturn mode',0
bpFeelReply db 64 dup(0)
bpFeelSampleAim dd -FP_1/2,-FP_1*45/100,-FP_1*4/10,-FP_1*35/100,-FP_1*3/10,-FP_1/4,-FP_1/5,-FP_1*15/100,-FP_1/10,-FP_1/20
dd 0,FP_1/20,FP_1/10,FP_1*15/100,FP_1/5,FP_1/4,FP_1*3/10,FP_1*35/100,FP_1*4/10,FP_1*45/100,FP_1/2
bpFeelSamplePower dd 0,50,100
bpFeelSamples dd 63*4 dup(0)
bpFeelSamplesPath db 'build\hall-expansion\bowling-physics-samples.bin',0
bpFeelSetterTimes dd 0,150,300,900,2000,3750,4150,4400
bpFeelEmptyPinPath db 'build\hall-expansion\bowling-shape-empty.bmp',0
bpFeelTinyPinPath db 'build\hall-expansion\bowling-shape-tiny.bmp',0
include tools/tests/bowling_hand_paths.inc
.code
BowlingFeelNativeTests PROC uses ebx esi edi
local window:DWORD,volume:DWORD,tilt:DWORD,aim:DWORD,power:DWORD,handle:DWORD,count:DWORD
mov expansionStage,170
call InitBowling3D
mov hallLevel3D,HALL_BOWLING
invoke BeginBowlingPlay3D,1
mov expansionStage,179
call ResetBowlingPlay3D
; Subpixel negative impulses must not collapse the pin's local basis.
invoke BowlingPlayKick3D,0,1,-1
cmp bpPinAxisX3D,0
jne bp_feel_fail
cmp bpPinAxisY3D,-FP_1
jne bp_feel_fail
invoke BowlingPlayKick3D,0,257,257
mov eax,bpPinAxisX3D
imul eax
shrd eax,edx,16
mov ebx,eax
mov eax,bpPinAxisY3D
imul eax
shrd eax,edx,16
add eax,ebx
cmp eax,FP_1*99/100
jb bp_feel_fail
cmp eax,FP_1*101/100
ja bp_feel_fail
mov bpState3D,BP_SETTLE
mov bpBallY3D,FP_1*28/10
mov bpPinTilt3D,128*256
mov bwRack3D[4],0
call RenderBowlingPlay3D
invoke ExpansionSnapshot,OFFSET bpFeelEmptyPinPath
mov bwRack3D[4],1
call RenderBowlingPlay3D
invoke ExpansionSnapshot,OFFSET bpFeelTinyPinPath
mov expansionStage,170
call ResetBowlingPlay3D
mov bwRack3D[4],1
mov bpGutter3D,1
mov bpPinVX3D,512
invoke BowlingPlayKick3D,0,512,0
cmp bpHitMask3D,0
jne bp_feel_fail
mov esi,200
bp_feel_weak:
call BowlingPlayStep3D
dec esi
jnz bp_feel_weak
cmp bpHitMask3D,0
jne bp_feel_fail
cmp bpPinTilt3D,24*256
jae bp_feel_fail
mov expansionStage,171
call ResetBowlingPlay3D
mov bwRack3D[4],1
mov bpGutter3D,1
mov bpPinVX3D,10000
invoke BowlingPlayKick3D,0,10000,0
cmp bpHitMask3D,0
jne bp_feel_fail
mov esi,120
bp_feel_topple:
call BowlingPlayStep3D
dec esi
jnz bp_feel_topple
test bpHitMask3D,1
jz bp_feel_fail
cmp bpPinTilt3D,100*256
jb bp_feel_fail
cmp bpPinX3D,FP_1*93/100
jg bp_feel_fail
mov expansionStage,172
; Opening contacts are separated but must not gain another impulse.
call ResetBowlingPlay3D
mov bpPinX3D,0
mov bpPinX3D[4],FP_1*15/100
mov bpPinY3D,4*FP_1
mov bpPinY3D[4],4*FP_1
mov bpPinVX3D,-2000
mov bpPinVX3D[4],2000
invoke BowlingPlayContact3D,0,1
cmp bpPinVX3D,-2000
jne bp_feel_fail
cmp bpPinVX3D[4],2000
jne bp_feel_fail
; A fallen pin can receive a second impact and transfer momentum again.
mov bpPinX3D,0
mov bpPinX3D[4],FP_1*15/100
mov bpPinVX3D,2000
mov bpPinVX3D[4],0
mov bpPinTilt3D,128*256
mov bpHitMask3D,1
invoke BowlingPlayContact3D,0,1
cmp bpPinVX3D[4],500
jb bp_feel_fail
cmp bpPinVX3D,2000
jae bp_feel_fail
mov expansionStage,173
call ResetBowlingPlay3D
mov bpEndTicks3D,100
mov bpPinTilt3D,40*256
call BowlingPlayReady3D
test eax,eax
jnz bp_feel_fail
mov bpPinTilt3D,128*256
call BowlingPlayReady3D
cmp eax,1
jne bp_feel_fail
mov expansionStage,174
call ResetBowlingPlay3D
lea esi,bpFeelScoreRolls
lea edi,bwRolls3D[192]
mov ecx,20
rep movsd
mov bwRollCount3D[8],20
invoke BowlingScore3D,2
invoke BowlingRollGlyph3D,2,0,0
cmp eax,'X'
jne bp_feel_fail
invoke BowlingRollGlyph3D,2,1,1
cmp eax,'/'
jne bp_feel_fail
invoke BowlingRollGlyph3D,2,2,0
cmp eax,'-'
jne bp_feel_fail
invoke BowlingRollGlyph3D,2,9,0
cmp eax,'X'
jne bp_feel_fail
invoke BowlingRollGlyph3D,2,9,1
cmp eax,'7'
jne bp_feel_fail
invoke BowlingRollGlyph3D,2,9,2
cmp eax,'/'
jne bp_feel_fail
mov bpScoreOpen3D,1
call RenderBowlingPlay3D
invoke ExpansionSnapshot,OFFSET bpFeelScorePath
mov bwRolls3D[192],0
mov bwRolls3D[196],10
mov bwRollCount3D[8],2
invoke BowlingRollGlyph3D,2,0,1
cmp eax,'/'
jne bp_feel_fail
lea esi,bpFeelScoreRolls
lea edi,bwRolls3D[192]
mov ecx,20
rep movsd
mov bwRollCount3D[8],20
mov bwRolls3D[192+18*4],0
mov bwRolls3D[192+19*4],10
invoke BowlingRollGlyph3D,2,9,2
cmp eax,'/'
jne bp_feel_fail
mov expansionStage,175
call ResetBowlingPlay3D
mov bpLockedAim3D,0
mov bpLockedPower3D,85
call BowlingPlayLaunch3D
mov bpState3D,BP_ROLL
mov esi,0
mov edi,0
bp_feel_impact:
call BowlingPlayStep3D
inc esi
cmp esi,160
jb bp_feel_impact
mov eax,esi
and eax,15
jnz bp_feel_impact
imul eax,esi,16
mov bpTime3D,eax
call RenderBowlingPlay3D
invoke ExpansionSnapshot,bpFeelPinPaths[edi*4]
inc edi
cmp edi,4
jb bp_feel_impact
mov power,0
mov aim,0
mov edi,0
bp_feel_sample:
call ResetBowlingPlay3D
mov eax,aim
mov eax,bpFeelSampleAim[eax*4]
mov bpLockedAim3D,eax
mov bpFeelSamples[edi],eax
mov eax,power
mov eax,bpFeelSamplePower[eax*4]
mov bpLockedPower3D,eax
mov bpFeelSamples[edi+4],eax
call BowlingPlayLaunch3D
mov esi,900
bp_feel_sample_steps:
call BowlingPlayStep3D
; Check the constraints throughout the roll, not just at settlement.
mov ebx,0
bp_feel_sample_bounds:
mov eax,bpPinX3D[ebx*4]
sub eax,bwPinX3D[ebx*4]
sar eax,8
imul eax,eax
mov ecx,eax
mov eax,bpPinY3D[ebx*4]
sub eax,bwPinY3D[ebx*4]
sar eax,8
imul eax,eax
add eax,ecx
cmp eax,132*132
ja bp_feel_fail
mov eax,bpPinAxisX3D[ebx*4]
imul eax
shrd eax,edx,16
mov ecx,eax
mov eax,bpPinAxisY3D[ebx*4]
imul eax
shrd eax,edx,16
add eax,ecx
cmp eax,FP_1*99/100
jb bp_feel_fail
cmp eax,FP_1*101/100
ja bp_feel_fail
inc ebx
cmp ebx,10
jb bp_feel_sample_bounds
dec esi
jnz bp_feel_sample_steps
mov eax,bpHitMask3D
mov bpFeelSamples[edi+8],eax
mov eax,bpPinTilt3D
mov bpFeelSamples[edi+12],eax
add edi,16
inc aim
cmp aim,21
jb bp_feel_sample
mov aim,0
inc power
cmp power,3
jb bp_feel_sample
invoke CreateFileA,OFFSET bpFeelSamplesPath,40000000h,0,0,2,80h,0
mov handle,eax
invoke WriteFile,handle,OFFSET bpFeelSamples,SIZEOF bpFeelSamples,ADDR count,0
invoke CloseHandle,handle
mov expansionStage,176
call ResetBowlingPlay3D
xor esi,esi
bp_feel_hand:
mov bpState3D,BP_RELEASE
imul eax,esi,33
mov bpTime3D,eax
cmp esi,21
jb @F
mov bpState3D,BP_ROLL
sub eax,21*33
mov bpTime3D,eax
@@:
call RenderBowlingPlay3D
invoke ExpansionSnapshot,bpFeelHandPaths[esi*4]
inc esi
cmp esi,33
jb bp_feel_hand
mov expansionStage,178
call ResetBowlingPlay3D
mov bpPinX3D,2*FP_1
mov bpPinVX3D,10000
invoke BowlingPlayTether3D,0
cmp bpPinX3D,FP_1*67/100
ja bp_feel_fail
cmp bpPinVX3D,2500
ja bp_feel_fail
mov bpPinTilt3D,128*256
mov bpPinAxisX3D,FP_1
mov bpPinAxisY3D,0
invoke BowlingPlayDeckBounds3D,0
cmp bpPinX3D,FP_1*48/100
ja bp_feel_fail
call ResetBowlingPlay3D
mov bpHitMask3D,1
mov bpPinTilt3D,128*256
call BowlingPlayFinishRoll3D
mov deltaTime3D,250
mov esi,7
bp_feel_start_setter:
call HandleBowlingPlay3D
dec esi
jnz bp_feel_start_setter
cmp bpState3D,BP_RESET
jne bp_feel_fail
cmp bwRollCount3D[8],1
jne bp_feel_fail
mov bpTime3D,1100
invoke BowlingPinsetterPose3D,0
test eax,eax
jz bp_feel_fail
cmp bpVisualPinLift3D,FP_1/2
jb bp_feel_fail
cmp bpPinTilt3D,128*256
jne bp_feel_fail
mov bpTime3D,1800
invoke BowlingPinsetterPose3D,0
cmp eax,1
jne bp_feel_fail
cmp bpVisualPinLift3D,FP_1
jb bp_feel_fail
mov bpTime3D,2450
invoke BowlingPinsetterPose3D,0
cmp eax,1
jne bp_feel_fail
cmp bpVisualPinLift3D,FP_1
jb bp_feel_fail
invoke BowlingPinsetterPose3D,1
cmp eax,1
jne bp_feel_fail
mov bwNewRack3D[4],1
invoke BowlingPinsetterPose3D,0
cmp eax,1
jne bp_feel_fail
mov bwNewRack3D[4],0
mov bpTime3D,3750
invoke BowlingPinsetterPose3D,1
cmp bpVisualPinLift3D,0
jne bp_feel_fail
mov esi,0
bp_feel_setter_capture:
mov eax,bpFeelSetterTimes[esi*4]
mov bpTime3D,eax
mov bpBallY3D,FP_1*28/10
call RenderBowlingPlay3D
invoke ExpansionSnapshot,bpFeelSetterPaths[esi*4]
inc esi
cmp esi,8
jb bp_feel_setter_capture
mov bpTime3D,BP_RESET_MS
mov deltaTime3D,0
call HandleBowlingPlay3D
cmp bpState3D,BP_RETURN
jne bp_feel_fail
mov bpTime3D,BP_RETURN_MS
call HandleBowlingPlay3D
cmp bpState3D,BP_DIRECTION
jne bp_feel_fail
cmp bwRack3D[4],1022
jne bp_feel_fail
cmp bwRollCount3D[8],1
jne bp_feel_fail
; First-ball miss leaves the standing rack alone and never runs the hoist.
call ResetBowlingPlay3D
call BowlingPlayFinishRoll3D
mov bpTime3D,1600
call HandleBowlingPlay3D
cmp bpState3D,BP_RETURN
jne bp_feel_fail
mov bpTime3D,BP_RETURN_MS
call HandleBowlingPlay3D
cmp bpState3D,BP_DIRECTION
jne bp_feel_fail
cmp bwRack3D[4],1023
jne bp_feel_fail
mov bpState3D,BP_RESET
mov bpTime3D,0
call BowlingPinsetterGate3D
cmp eax,FP_1
jne bp_feel_fail
mov bpTime3D,400
call BowlingPinsetterGate3D
cmp eax,FP_1*14/100
ja bp_feel_fail
mov bpTime3D,4400
call BowlingPinsetterGate3D
cmp eax,FP_1
jne bp_feel_fail
mov expansionStage,177
; Real MCI devices, muted: opens, volume, release/roll/impact and close.
mov eax,h3DWnd
mov window,eax
mov eax,settingsVolume
mov volume,eax
mov h3DWnd,1
mov settingsVolume,0
call BowlingAudioOpen3D
mov bpScoreOpen3D,0
mov bpState3D,BP_ROLL
mov bpEndTicks3D,0
mov bpImpactEnergy3D,6000
mov bpImpactCooldown3D,0
call BowlingAudioUpdate3D
invoke mciSendStringA,OFFSET bpFeelStatus,OFFSET bpFeelReply,64,0
test eax,eax
jnz bp_feel_audio_fail
cmp BYTE PTR bpFeelReply,'p'
jne bp_feel_audio_fail
invoke mciSendStringA,OFFSET bpFeelStatusLight,OFFSET bpFeelReply,64,0
test eax,eax
jnz bp_feel_audio_fail
invoke mciSendStringA,OFFSET bpFeelStatusReset,OFFSET bpFeelReply,64,0
test eax,eax
jnz bp_feel_audio_fail
mov bpScoreOpen3D,1
call BowlingAudioUpdate3D
cmp bpAudioRolling3D,0
jne bp_feel_audio_fail
mov bpScoreOpen3D,0
mov bpState3D,BP_RETURN
mov bpTime3D,500
call BowlingAudioUpdate3D
invoke mciSendStringA,OFFSET bpFeelStatusReturn,OFFSET bpFeelReply,64,0
test eax,eax
jnz bp_feel_audio_fail
cmp BYTE PTR bpFeelReply,'p'
jne bp_feel_audio_fail
mov bpScoreOpen3D,1
call BowlingAudioUpdate3D
cmp bpAudioReturning3D,0
jne bp_feel_audio_fail
call BowlingAudioClose3D
invoke mciSendStringA,OFFSET bpFeelStatus,OFFSET bpFeelReply,64,0
test eax,eax
jz bp_feel_audio_fail
invoke mciSendStringA,OFFSET bpFeelStatusReturn,OFFSET bpFeelReply,64,0
test eax,eax
jz bp_feel_audio_fail
mov eax,window
mov h3DWnd,eax
mov eax,volume
mov settingsVolume,eax
call LeaveBowlingPlay3D
ret
bp_feel_audio_fail:
call BowlingAudioClose3D
bp_feel_fail:
invoke ExitProcess,expansionStage
ret
BowlingFeelNativeTests ENDP