KAZIK/tools/tests/npc_visits_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

268 lines
7.1 KiB
PHP

.data
nvTestPath db 'build\hall-expansion\npc-visits.bin',0
nvTestOrder db 'build\hall-expansion\npc-visit-order.bmp',0
nvTestDrink db 'build\hall-expansion\npc-visit-drink.bmp',0
nvTestSeat db 'build\hall-expansion\npc-visit-seat.bmp',0
nvTestWin db 'build\hall-expansion\npc-visit-win.bmp',0
nvTestLoss db 'build\hall-expansion\npc-visit-loss.bmp',0
; slot, identity, cast, activities, destinations, walking ticks, journeys, chips.
nvTestRecords dd NPC_LIFE_COUNT*8 dup(0)
.code
NpcVisitsNativeTests PROC uses ebx esi edi
local ticks:DWORD,bank:DWORD,rng:DWORD,handle:DWORD,count:DWORD,bits:DWORD
local savedClock:DWORD,seenResults:DWORD
mov expansionStage,200
mov hallLevel3D,0
mov engineMode,MODE_WALK
mov worldMap3D,OFFSET casinoMap
mov playerX,FP_1*305/10
mov playerY,FP_1*305/10
mov eax,bankroll
mov bank,eax
mov eax,seed
mov rng,eax
call NpcChairMotionNativeTests
call InitNpcLife3D
mov bits,0
xor esi,esi
nv_test_identity:
imul ebx,esi,SIZEOF NpcLife3D
imul edi,esi,32
mov nvTestRecords[edi],esi
mov eax,npcLife3D[ebx].identity
mov nvTestRecords[edi+4],eax
mov eax,npcGuestAppearance3D[eax*4]
mov nvTestRecords[edi+8],eax
bts bits,eax
jc nv_test_fail
inc esi
cmp esi,NPC_LIFE_COUNT
jb nv_test_identity
cmp bits,1023
jne nv_test_fail
xor esi,esi
nv_test_paths:
invoke NpcPlanPath3D,0,esi
cmp eax,1
jne nv_test_fail
inc esi
cmp esi,NPC_DEST_COUNT
jb nv_test_paths
call InitNpcLife3D
mov expansionStage,201
mov deltaTime3D,50
mov ticks,9600
nv_test_tick:
call UpdateNpcLife3D
xor esi,esi
nv_test_person:
imul ebx,esi,SIZEOF NpcLife3D
imul edi,esi,32
mov eax,npcLife3D[ebx].identity
cmp eax,nvTestRecords[edi+4]
jne nv_test_fail
cmp npcVisitChips3D[esi*4],0
jl nv_test_fail
cmp npcVisitChips3D[esi*4],100000
ja nv_test_fail
mov eax,npcVisitActivity3D[esi*4]
cmp eax,NV_YIELD
ja nv_test_fail
bts nvTestRecords[edi+12],eax
mov eax,npcLife3D[ebx].destination
cmp eax,NPC_DEST_COUNT
jae nv_test_fail
cmp npcLife3D[ebx].state,NPC_WALK
jne @F
inc nvTestRecords[edi+20]
jmp nv_test_bounds
@@:
cmp npcVisitActivity3D[esi*4],NV_IDLE
je nv_test_bounds
cmp npcVisitActivity3D[esi*4],NV_YIELD
je nv_test_bounds
bts nvTestRecords[edi+16],eax
nv_test_bounds:
invoke NpcEntity3D,esi
mov ebx,eax
invoke CanWalk3D,[ebx].NpcMindEntity.posX,[ebx].NpcMindEntity.posY
cmp eax,1
jne nv_test_fail
inc esi
cmp esi,NPC_LIFE_COUNT
jb nv_test_person
xor esi,esi
mov bits,0
nv_test_station:
mov eax,npcStationOwner3D[esi*4]
cmp eax,-1
je nv_test_station_next
cmp eax,NPC_LIFE_COUNT
jae nv_test_fail
bts bits,eax
jc nv_test_fail
imul ebx,eax,SIZEOF NpcLife3D
cmp npcLife3D[ebx].destination,esi
jne nv_test_fail
cmp npcLife3D[ebx].state,NPC_AWAY
je nv_test_fail
nv_test_station_next:
inc esi
cmp esi,NPC_DEST_COUNT
jb nv_test_station
dec ticks
jnz nv_test_tick
mov expansionStage,202
xor esi,esi
nv_test_record:
imul edi,esi,32
imul ebx,esi,SIZEOF NpcLife3D
mov eax,npcLife3D[ebx].routeStep
mov nvTestRecords[edi+24],eax
mov eax,npcVisitChips3D[esi*4]
mov nvTestRecords[edi+28],eax
cmp nvTestRecords[edi+20],50
jb nv_test_fail
inc esi
cmp esi,NPC_LIFE_COUNT
jb nv_test_record
invoke CreateFileA,OFFSET nvTestPath,40000000h,0,0,2,80h,0
mov handle,eax
invoke WriteFile,handle,OFFSET nvTestRecords,SIZEOF nvTestRecords,ADDR count,0
invoke CloseHandle,handle
mov eax,bankroll
cmp eax,bank
jne nv_test_fail
mov eax,seed
cmp eax,rng
jne nv_test_fail
mov eax,npcLifeClock3D
mov savedClock,eax
mov engineMode,MODE_NPC_DIALOG
call UpdateNpcLife3D
mov eax,savedClock
cmp npcLifeClock3D,eax
jne nv_test_fail
mov engineMode,MODE_WALK
mov hallLevel3D,HALL_ROOF
call UpdateNpcLife3D
mov eax,savedClock
cmp npcLifeClock3D,eax
jne nv_test_fail
mov hallLevel3D,0
mov expansionStage,204
call InitNpcLife3D
mov esi,1
nv_test_yield_clear:
imul ebx,esi,SIZEOF NpcLife3D
mov npcLife3D[ebx].state,NPC_AWAY
inc esi
cmp esi,NPC_LIFE_COUNT
jb nv_test_yield_clear
invoke NpcReleaseStation3D,0
invoke NpcPlanPath3D,0,5
cmp eax,1
jne nv_test_fail
mov npcStationOwner3D[5*4],0
mov npcLife3D.state,NPC_WALK
mov eax,casinoNpcs3D.posX
mov playerX,eax
mov eax,casinoNpcs3D.posY
mov playerY,eax
invoke NpcMove3D,0,16
cmp npcVisitActivity3D,NV_YIELD
jne nv_test_fail
cmp npcLife3D.pathCount,0
je nv_test_fail
mov ticks,7
nv_test_yield_retry:
invoke NpcAdvanceVisit3D,0
invoke NpcMove3D,0,16
dec ticks
jnz nv_test_yield_retry
cmp npcLife3D.pathCount,0
jne nv_test_fail
cmp npcStationOwner3D[5*4],-1
jne nv_test_fail
cmp npcWalkDistance3D,0
jne nv_test_fail
mov expansionStage,203
call InitNpcLife3D
xor esi,esi
nv_test_hide:
imul ebx,esi,SIZEOF NpcLife3D
mov npcLife3D[ebx].state,NPC_AWAY
inc esi
cmp esi,NPC_LIFE_COUNT
jb nv_test_hide
mov npcLife3D.destination,10
mov casinoNpcs3D.posX,FP_1*65/10
mov casinoNpcs3D.posY,FP_1*255/10
invoke NpcArrive3D,0
mov playerX,FP_1*65/10
mov playerY,FP_1*282/10
mov playerAngle,384
call UpdateInteriorEye3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET nvTestOrder
invoke NpcAdvanceVisit3D,0
cmp npcVisitActivity3D,NV_DRINK
jne nv_test_fail
mov npcLife3D.poseClock,360
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET nvTestDrink
mov npcLife3D.destination,7
mov casinoNpcs3D.posX,FP_1*135/10
mov casinoNpcs3D.posY,FP_1*195/10
invoke NpcArrive3D,0
mov npcLife3D.poseClock,1200
mov npcSitTime3D,800
mov playerX,13*FP_1
mov playerY,FP_1*215/10
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET nvTestSeat
mov npcSprite3D.posX,FP_1*135/10
mov npcSprite3D.posY,FP_1*195/10
invoke NpcResolveVisitPose3D,0
cmp eax,CAST_SEAT
jne nv_test_fail
cmp npcSprite3D.posX,13*FP_1
jne nv_test_fail
cmp npcSprite3D.posY,FP_1*195/10-FP_1*35/100
jne nv_test_fail
mov seenResults,0
mov ticks,32
mov npcLife3D.destination,2
mov casinoNpcs3D.posX,FP_1*105/10
mov casinoNpcs3D.posY,FP_1*135/10
mov playerX,FP_1*105/10
mov playerY,16*FP_1
nv_test_result:
invoke NpcArrive3D,0
invoke NpcAdvanceVisit3D,0
mov npcLife3D.poseClock,360
call ExpansionFrame
cmp npcVisitActivity3D,NV_WIN
jne nv_test_loss
invoke ExpansionSnapshot,OFFSET nvTestWin
or seenResults,1
jmp nv_test_result_next
nv_test_loss:
cmp npcVisitActivity3D,NV_LOSS
jne nv_test_fail
invoke ExpansionSnapshot,OFFSET nvTestLoss
or seenResults,2
nv_test_result_next:
cmp seenResults,3
je nv_test_done
dec ticks
jnz nv_test_result
jmp nv_test_fail
nv_test_done:
call InitNpcLife3D
ret
nv_test_fail:
invoke ExitProcess,expansionStage
ret
NpcVisitsNativeTests ENDP
include tools/tests/npc_chair_motion_test.inc