KAZIK/tools/tests/hall_expansion_test.inc
Artvasil09 51c6512675 feat: implement secret fortune teller room, lived-in den v3, and bar camera stabilization
- Secret Fortune Teller Room: arcade lantern toggle, sliding timber panel, dedicated staircase and compact cellar chamber

- Lived-in den pass v3: cloth hangings, draped workbench, props/clutter, centered entrance, and custom light sources

- Bar camera stabilization: remove alcohol horizon sway while retaining drink animations

- Synchronize binaries and update launcher hash to EADBA9C8C9BD5B7E3CC4CE486ED647C1CE9383D1DCB6EBC94FE755EA1F4BF927
2026-10-20 17:29:52 +03:00

1171 lines
34 KiB
PHP

.data
expansionStage dd 1
expansionOcclusionPixel dd 0
expansionGaitX dd 0
expansionGaitY dd 0
expansionGaitPhase dd 0
expansionDoorState dd 0
expansionBmpHeader db 42h,4Dh
dd 1228854
dw 0,0
dd 54,40,640,-480
dw 1,32
dd 0,1228800,0,0,0,0
expansionDoor db "build\hall-expansion\basement-door.bmp",0
expansionDoorWheel db "build\hall-expansion\door-wheel-turn.bmp",0
expansionDoorSwing db "build\hall-expansion\door-swing-half.bmp",0
expansionDoorClosing db "build\hall-expansion\door-closing.bmp",0
expansionDoorLocking db "build\hall-expansion\door-locking.bmp",0
expansionDoorShut db "build\hall-expansion\door-closed-again.bmp",0
expansionDoorBack db "build\hall-expansion\door-from-stairs.bmp",0
expansionFarUpper db "build\hall-expansion\upper-return-far.bmp",0
expansionFarEntry db "build\hall-expansion\upper-entry-far.bmp",0
expansionFarBasement db "build\hall-expansion\basement-return-far.bmp",0
expansionGait0 db "build\hall-expansion\gait-0.bmp",0
expansionGait1 db "build\hall-expansion\gait-1.bmp",0
expansionGait2 db "build\hall-expansion\gait-2.bmp",0
expansionGait3 db "build\hall-expansion\gait-3.bmp",0
expansionGait4 db "build\hall-expansion\gait-4.bmp",0
expansionGait5 db "build\hall-expansion\gait-5.bmp",0
expansionGait6 db "build\hall-expansion\gait-6.bmp",0
expansionGait7 db "build\hall-expansion\gait-7.bmp",0
expansionGaitPaths dd OFFSET expansionGait0,OFFSET expansionGait1,OFFSET expansionGait2,OFFSET expansionGait3
dd OFFSET expansionGait4,OFFSET expansionGait5,OFFSET expansionGait6,OFFSET expansionGait7
expansionStairs db "build\hall-expansion\upper-stairs.bmp",0
expansionStairsMid db "build\hall-expansion\upper-stairs-mid.bmp",0
expansionUpper db "build\hall-expansion\upper-lounge.bmp",0
expansionUpperBar db "build\hall-expansion\upper-bar.bmp",0
expansionJazz db "build\hall-expansion\cabaret-jazz.bmp",0
expansionJazz1 db "build\hall-expansion\cabaret-jazz-1.bmp",0
expansionJazz2 db "build\hall-expansion\cabaret-jazz-2.bmp",0
expansionJazz3 db "build\hall-expansion\cabaret-jazz-3.bmp",0
expansionChorus db "build\hall-expansion\cabaret-chorus.bmp",0
expansionCast db "build\hall-expansion\guest-cast.bmp",0
expansionUpEntry db "build\hall-expansion\upper-entrance-open.bmp",0
expansionUpReturn db "build\hall-expansion\upper-stair-return.bmp",0
expansionUpLanding db "build\hall-expansion\upper-landing-open.bmp",0
expansionUpExit db "build\hall-expansion\upper-exit-open.bmp",0
expansionDownClosed db "build\hall-expansion\old-basement-closed.bmp",0
expansionDownReturn db "build\hall-expansion\basement-stair-return.bmp",0
expansionDownLanding db "build\hall-expansion\basement-landing-open.bmp",0
expansionBunkerReturn db "build\hall-expansion\bunker-return-open.bmp",0
expansionPortalClosed db "build\hall-expansion\portal-closed.bmp",0
expansionBuckshotTable db "build\hall-expansion\upper-buckshot-table.bmp",0
expansionRussianTable db "build\hall-expansion\upper-russian-table.bmp",0
expansionBarWorld db "build\hall-expansion\upper-bar-world.bmp",0
expansionSeating db "build\hall-expansion\upper-seating.bmp",0
expansionLeverMid db "build\hall-expansion\lever-mid-pull.bmp",0
expansionPortalOff db "build\hall-expansion\portal-disabled.bmp",0
expansionLeverRelease db "build\hall-expansion\lever-mid-release.bmp",0
expansionPortal0 db "build\hall-expansion\portal-00.bmp",0
expansionPortal1 db "build\hall-expansion\portal-01.bmp",0
expansionPortal2 db "build\hall-expansion\portal-02.bmp",0
expansionPortal3 db "build\hall-expansion\portal-03.bmp",0
expansionPortal4 db "build\hall-expansion\portal-04.bmp",0
expansionPortal5 db "build\hall-expansion\portal-05.bmp",0
expansionPortal6 db "build\hall-expansion\portal-06.bmp",0
expansionPortal7 db "build\hall-expansion\portal-07.bmp",0
expansionPortal8 db "build\hall-expansion\portal-08.bmp",0
expansionPortal9 db "build\hall-expansion\portal-09.bmp",0
expansionPortal10 db "build\hall-expansion\portal-10.bmp",0
expansionPortal11 db "build\hall-expansion\portal-11.bmp",0
expansionPortalPaths dd OFFSET expansionPortal0,OFFSET expansionPortal1,OFFSET expansionPortal2,OFFSET expansionPortal3,OFFSET expansionPortal4,OFFSET expansionPortal5
dd OFFSET expansionPortal6,OFFSET expansionPortal7,OFFSET expansionPortal8,OFFSET expansionPortal9,OFFSET expansionPortal10,OFFSET expansionPortal11
.data?
expansionSinglePixels dd SCREEN_SIZE dup(?)
.code
ExpansionSnapshot PROC uses ebx path:DWORD
local count:DWORD
invoke CreateFileA,path,40000000h,0,0,2,80h,0
cmp eax,-1
je expansion_snapshot_fail
mov ebx,eax
invoke WriteFile,ebx,OFFSET expansionBmpHeader,54,ADDR count,0
invoke WriteFile,ebx,OFFSET screenBuffer,1228800,ADDR count,0
invoke CloseHandle,ebx
ret
expansion_snapshot_fail:
invoke ExitProcess,99
ret
ExpansionSnapshot ENDP
ExpansionFrame PROC
call UpdateCamera3D
call RenderCeilingAndFloor
call RenderWalls
call PrepareCityDepth3D
call RenderSprites
call RenderHall3D
ret
ExpansionFrame ENDP
ExpansionWalkFrame PROC
add animationClock3D,16
call UpdateInteriorTravel3D
call HandlePlayerInput
call UpdateInteriorTravel3D
call UpdateInteriorEye3D
ret
ExpansionWalkFrame ENDP
ExpansionPortalSnapshot PROC path:DWORD
call UpdateInteriorEye3D
call ExpansionFrame
cmp portalPixelsWritten3D,1000
jae @F
invoke ExitProcess,expansionStage
@@:
invoke ExpansionSnapshot,path
ret
ExpansionPortalSnapshot ENDP
ExpansionDoorTicks PROC uses esi count:DWORD
mov esi,count
expansion_door_tick:
add animationClock3D,16
call UpdateBasementDoor3D
dec esi
jnz expansion_door_tick
ret
ExpansionDoorTicks ENDP
ExpansionTestStart PROC
call InitPaths3D
call LoadTextures3D
call InitializeCasinoSprites3D
call InitHall3D
call LoadHallAssets3D
cmp npcGuestArt3D.pixels,0
je expansion_fail
cmp npcWalkArt3D.frames,48
jne expansion_fail
cmp npcWalkLeftArt3D.frames,48
jne expansion_fail
cmp basementDoorArt3D.frames,18
jne expansion_fail
cmp cabaretArt3D.pixels,0
je expansion_fail
cmp cabaretPianoArt3D.pixels,0
je expansion_fail
cmp cabaretGuitarArt3D.pixels,0
je expansion_fail
cmp upperPortalArt3D.frames,13
jne expansion_fail
cmp upperLeverArt3D.frames,6
jne expansion_fail
cmp upperBrazierArt3D.frames,4
jne expansion_fail
cmp scrStation3D.posX,29*FP_1
jne expansion_fail
cmp scrStation3D.posY,FP_1*15/2
jne expansion_fail
cmp upperFeltPixels3D[0],0FF1B5039h
jb expansion_fail
cmp upperKeyPixels3D[0],80000000h
jb expansion_fail
mov deltaTime3D,16
mov engineMode,MODE_WALK
mov hallLevel3D,0
mov expansionStage,2
invoke CanWalk3D,FP_1*289/10,FP_1*105/10
test eax,eax
jnz expansion_fail
mov playerX,27*FP_1
mov playerY,FP_1*105/10
mov playerAngle,0
add animationClock3D,16
call UpdateInteriorTravel3D
cmp engineMode,MODE_WALK
jne expansion_fail
cmp basementDoorTarget3D,FP_1
jne expansion_fail
cmp basementDoorOpen3D,0
jne expansion_fail
xor esi,esi
expansion_open_door:
add animationClock3D,16
call UpdateInteriorTravel3D
inc esi
cmp esi,20
jb expansion_open_door
cmp basementWheelTime3D,0
je expansion_fail
cmp basementDoorOpen3D,0
jne expansion_fail
call UpdateInteriorEye3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionDoorWheel
mov esi,50
expansion_door_swing:
add animationClock3D,16
call UpdateInteriorTravel3D
dec esi
jnz expansion_door_swing
cmp basementWheelTime3D,700
jne expansion_fail
cmp basementDoorOpen3D,0
je expansion_fail
cmp basementDoorOpen3D,FP_1
jae expansion_fail
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionDoorSwing
mov esi,50
expansion_finish_door:
add animationClock3D,16
call UpdateInteriorTravel3D
dec esi
jnz expansion_finish_door
cmp basementDoorOpen3D,FP_1
jne expansion_fail
invoke CanWalk3D,FP_1*289/10,FP_1*105/10
cmp eax,1
jne expansion_fail
mov playerX,27*FP_1
mov playerY,FP_1*105/10
mov playerAngle,0
call UpdateInteriorEye3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionDoor
mov expansionStage,3
mov keys3D[57h],1
xor esi,esi
expansion_down:
cmp engineMode,MODE_STAIRS
jne @F
cmp playerX,FP_1*165/10
jb @F
mov playerAngle,384
@@:
call ExpansionWalkFrame
cmp engineMode,MODE_WALK
jne @F
cmp hallLevel3D,1
je expansion_at_bunker
@@:
inc esi
cmp esi,800
jb expansion_down
jmp expansion_fail
expansion_at_bunker:
mov keys3D[57h],0
cmp playerAngle,384
jne expansion_fail
cmp worldMap3D,OFFSET bunkerMap3D
jne expansion_fail
mov expansionStage,4
mov playerAngle,128
mov keys3D[57h],1
xor esi,esi
expansion_up_basement:
cmp engineMode,MODE_STAIRS
jne @F
cmp playerY,21*FP_1
jb @F
mov playerAngle,256
@@:
call ExpansionWalkFrame
cmp engineMode,MODE_WALK
jne @F
cmp hallLevel3D,0
je expansion_back_hall
@@:
inc esi
cmp esi,800
jb expansion_up_basement
jmp expansion_fail
expansion_back_hall:
mov keys3D[57h],0
cmp playerX,28*FP_1-FP_1/10
jb expansion_fail
cmp playerX,FP_1*282/10
ja expansion_fail
mov expansionStage,5
mov playerX,FP_1*294/10
mov playerY,21*FP_1
mov playerAngle,0
mov keys3D[57h],1
xor esi,esi
expansion_upper_walk:
call ExpansionWalkFrame
cmp engineMode,MODE_WALK
jne @F
cmp hallLevel3D,2
je expansion_at_upper
@@:
inc esi
cmp esi,1100
jb expansion_upper_walk
jmp expansion_fail
expansion_at_upper:
mov keys3D[57h],0
cmp worldMap3D,OFFSET upperLoungeMap3D
jne expansion_fail
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionUpper
mov expansionStage,6
mov playerX,7*FP_1
mov playerY,FP_1*187/10
mov playerAngle,384
call UpdateCamera3D
call CheckInteractions
cmp interactTargetId,INTERACT_BUCKSHOT
jne expansion_fail
mov playerX,12*FP_1
mov playerY,FP_1*197/10
call CheckInteractions
cmp interactTargetId,INTERACT_RUSSIAN_ROULETTE
jne expansion_fail
mov playerX,9*FP_1
mov playerY,FP_1*221/10
mov playerX,FP_1*91/10
mov playerAngle,128
call UpdateCamera3D
call CheckInteractions
cmp interactTargetId,INTERACT_UPPER_LEFT
jne expansion_fail
mov playerX,13*FP_1
mov playerY,FP_1*163/10
mov playerAngle,384
call UpdateCamera3D
call CheckInteractions
cmp interactTargetId,INTERACT_BAR
jne expansion_fail
call TriggerHallInteraction3D
cmp engineMode,MODE_BAR
jne expansion_fail
call RenderBar3D
cmp playerX,13*FP_1
jne expansion_fail
cmp hallLevel3D,2
jne expansion_fail
invoke ExpansionSnapshot,OFFSET expansionUpperBar
mov expansionStage,7
mov engineMode,MODE_WALK
mov playerX,4*FP_1
mov playerY,21*FP_1
mov playerAngle,256
mov keys3D[57h],1
xor esi,esi
expansion_descend_upper:
call ExpansionWalkFrame
cmp engineMode,MODE_WALK
jne @F
cmp hallLevel3D,0
je expansion_upper_returned
@@:
inc esi
cmp esi,1100
jb expansion_descend_upper
jmp expansion_fail
expansion_upper_returned:
mov keys3D[57h],0
; No key held: neither position nor floor changes even after a long wait.
mov engineMode,MODE_STAIRS
mov stairRoute3D,2
mov worldMap3D,OFFSET upperStairMap3D
mov floorTexture3D,85
mov playerX,7*FP_1
mov playerY,21*FP_1
mov playerAngle,0
mov deltaTime3D,5000
call HandleStairs3D
cmp playerX,7*FP_1
jne expansion_fail
cmp engineMode,MODE_STAIRS
jne expansion_fail
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionStairs
mov playerX,11*FP_1
call UpdateStairHeight3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionStairsMid
mov expansionStage,8
mov deltaTime3D,16
mov hallLevel3D,0
mov worldMap3D,OFFSET casinoMap
mov floorTexture3D,100
mov playerX,16*FP_1
mov playerY,FP_1*67/10
mov playerAngle,384
mov cabaretTime3D,30000
call UpdateCabaret3D
call BeginCabaret3D
mov animationClock3D,0
call RenderCabaret3D
invoke ExpansionSnapshot,OFFSET expansionJazz
mov animationClock3D,200
call RenderCabaret3D
cmp cabaretPiano3D.frame,1
jne expansion_fail
cmp cabaretGuitar3D.frame,1
jne expansion_fail
invoke ExpansionSnapshot,OFFSET expansionJazz1
mov animationClock3D,400
call RenderCabaret3D
invoke ExpansionSnapshot,OFFSET expansionJazz2
mov animationClock3D,600
call RenderCabaret3D
cmp cabaretPiano3D.frame,3
jne expansion_fail
cmp cabaretGuitar3D.frame,3
jne expansion_fail
invoke ExpansionSnapshot,OFFSET expansionJazz3
mov cabaretTime3D,0
call UpdateCabaret3D
call RenderCabaret3D
invoke ExpansionSnapshot,OFFSET expansionChorus
mov pressedKeys3D[45h],1
call HandleCabaret3D
cmp engineMode,MODE_WALK
jne expansion_fail
mov bankroll,100
call BeginCabaret3D
mov mouseClick3D,1
mov mouseX3D,40
mov mouseY3D,220
call HandleCabaret3D
cmp bankroll,0
jne expansion_fail
mov mouseClick3D,1
call HandleCabaret3D
cmp bankroll,0
jne expansion_fail
mov mouseClick3D,1
mov mouseX3D,280
call HandleCabaret3D
cmp engineMode,MODE_WALK
jne expansion_fail
mov expansionStage,9
; Each visitor maps to their own frame block, not the first man's idle pose.
mov playerX,16*FP_1
mov playerY,20*FP_1
mov playerAngle,384
xor esi,esi
expansion_cast_positions:
imul ebx,esi,SIZEOF NpcMindEntity
imul eax,esi,FP_1
add eax,FP_1*27/2
mov casinoNpcs3D[ebx].posX,eax
mov casinoNpcs3D[ebx].posY,16*FP_1
imul ebx,esi,SIZEOF NpcLife3D
mov npcLife3D[ebx].identity,esi
mov npcLife3D[ebx].state,NPC_STAND
inc esi
cmp esi,6
jb expansion_cast_positions
call UpdateInteriorEye3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionCast
mov hallLevel3D,2
mov worldMap3D,OFFSET upperLoungeMap3D
mov floorTexture3D,124
mov playerX,7*FP_1
mov playerY,FP_1*194/10
mov playerAngle,384
call UpdateInteriorEye3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionBuckshotTable
mov playerX,12*FP_1
mov playerY,FP_1*204/10
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionRussianTable
mov playerX,FP_1*125/10
mov playerY,FP_1*168/10
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionBarWorld
mov playerX,FP_1*56/10
mov playerY,FP_1*205/10
mov playerAngle,128
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionSeating
mov expansionStage,20
mov hallLevel3D,0
mov worldMap3D,OFFSET casinoMap
mov floorTexture3D,100
mov playerX,FP_1*285/10
mov playerY,21*FP_1
mov playerAngle,0
invoke ExpansionPortalSnapshot,OFFSET expansionUpEntry
invoke DrawCityVertical3D,0,29*FP_1,FP_1*208/10,FP_1*212/10,0,FP_1*22/10,122
mov eax,screenBuffer[(240*SCREEN_W+320)*4]
mov expansionOcclusionPixel,eax
call RenderInteriorViews3D
mov eax,screenBuffer[(240*SCREEN_W+320)*4]
cmp eax,expansionOcclusionPixel
jne expansion_fail
mov expansionStage,21
mov engineMode,MODE_STAIRS
mov stairRoute3D,2
mov worldMap3D,OFFSET upperStairMap3D
mov floorTexture3D,85
mov playerX,FP_1*48/10
mov playerAngle,256
invoke ExpansionPortalSnapshot,OFFSET expansionUpReturn
mov expansionStage,22
mov playerX,15*FP_1
mov playerAngle,0
invoke ExpansionPortalSnapshot,OFFSET expansionUpLanding
mov expansionStage,23
mov engineMode,MODE_WALK
mov hallLevel3D,2
mov worldMap3D,OFFSET upperLoungeMap3D
mov floorTexture3D,124
mov playerX,FP_1*45/10
mov playerAngle,256
invoke ExpansionPortalSnapshot,OFFSET expansionUpExit
mov expansionStage,24
mov hallLevel3D,0
mov worldMap3D,OFFSET casinoMap
mov floorTexture3D,100
mov playerX,FP_1*267/10
mov playerY,FP_1*105/10
mov playerAngle,0
mov basementDoorOpen3D,0
call UpdateInteriorEye3D
call ExpansionFrame
cmp portalPixelsWritten3D,0
jne expansion_fail
invoke ExpansionSnapshot,OFFSET expansionDownClosed
mov basementDoorOpen3D,FP_1
invoke ExpansionPortalSnapshot,OFFSET expansionDoor
mov expansionStage,25
mov engineMode,MODE_STAIRS
mov stairRoute3D,1
mov worldMap3D,OFFSET basementStairMap3D
mov floorTexture3D,94
mov playerX,FP_1*45/10
mov playerY,21*FP_1
mov playerAngle,256
invoke ExpansionPortalSnapshot,OFFSET expansionDownReturn
mov expansionStage,26
mov playerX,FP_1*165/10
mov playerY,FP_1*155/10
mov playerAngle,384
invoke ExpansionPortalSnapshot,OFFSET expansionDownLanding
mov expansionStage,27
mov engineMode,MODE_WALK
mov hallLevel3D,1
mov worldMap3D,OFFSET bunkerMap3D
mov playerY,FP_1*265/10
mov playerAngle,128
invoke ExpansionPortalSnapshot,OFFSET expansionBunkerReturn
mov expansionStage,28
; These viewpoints used to lose their continuation at the eight-metre cutoff.
mov engineMode,MODE_STAIRS
mov stairRoute3D,2
mov worldMap3D,OFFSET upperStairMap3D
mov floorTexture3D,85
mov playerX,12*FP_1
mov playerY,21*FP_1
mov playerAngle,256
call UpdateInteriorEye3D
call ExpansionFrame
cmp portalPixelsWritten3D,100
jb expansion_fail
invoke ExpansionSnapshot,OFFSET expansionFarUpper
mov engineMode,MODE_WALK
mov hallLevel3D,0
mov worldMap3D,OFFSET casinoMap
mov floorTexture3D,100
mov playerX,21*FP_1
mov playerAngle,0
call UpdateInteriorEye3D
call ExpansionFrame
cmp portalPixelsWritten3D,100
jb expansion_fail
invoke ExpansionSnapshot,OFFSET expansionFarEntry
mov engineMode,MODE_STAIRS
mov stairRoute3D,1
mov worldMap3D,OFFSET basementStairMap3D
mov floorTexture3D,94
mov playerX,12*FP_1
mov playerAngle,256
call UpdateInteriorEye3D
call ExpansionFrame
cmp portalPixelsWritten3D,100
jb expansion_fail
invoke ExpansionSnapshot,OFFSET expansionFarBasement
mov engineMode,MODE_WALK
mov hallLevel3D,2
mov worldMap3D,OFFSET upperLoungeMap3D
mov floorTexture3D,124
mov playerX,FP_1*105/10
mov playerY,FP_1*193/10
mov playerAngle,128
mov upperLeverMask3D,0
mov upperPortalReady3D,0
mov upperLeverTime3D,0
mov upperLeverTime3D[4],0
call UpdateInteriorEye3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionPortalClosed
mov interactTargetId,INTERACT_UPPER_LEFT
call TriggerHallInteraction3D
mov esi,40
expansion_first_lever:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_first_lever
cmp upperLeverTime3D,600
jne expansion_fail
cmp upperLeverTime3D[4],0
jne expansion_fail
cmp upperPortalReady3D,0
jne expansion_fail
mov playerY,FP_1*244/10
call TryWalkUpperPortal3D
cmp engineMode,MODE_WALK
jne expansion_fail
mov playerY,FP_1*193/10
mov interactTargetId,INTERACT_UPPER_RIGHT
call TriggerHallInteraction3D
mov esi,20
expansion_second_lever_half:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_second_lever_half
cmp upperPortalReady3D,0
jne expansion_fail
call ExpansionFrame
cmp upperLever3D.frame,2
jne expansion_fail
invoke ExpansionSnapshot,OFFSET expansionLeverMid
mov esi,20
expansion_second_lever_end:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_second_lever_end
cmp upperPortalReady3D,1
jne expansion_fail
xor esi,esi
expansion_portal_motion:
imul eax,esi,80
mov animationClock3D,eax
call ExpansionFrame
mov eax,esi
inc eax
cmp upperPortal3D.frame,eax
jne expansion_fail
invoke ExpansionSnapshot,expansionPortalPaths[esi*4]
inc esi
cmp esi,12
jb expansion_portal_motion
mov expansionStage,29
mov playerX,FP_1*91/10
mov playerY,FP_1*214/10
mov playerAngle,128
call UpdateCamera3D
call CheckUpperInteractions3D
cmp interactTargetId,INTERACT_UPPER_LEFT
jne expansion_fail
cmp interactPromptPtr,OFFSET upperLeftRelease3D
jne expansion_fail
call TriggerHallInteraction3D
cmp upperLeverMask3D,2
jne expansion_fail
cmp upperPortalReady3D,0
jne expansion_fail
mov playerX,FP_1*105/10
mov playerY,FP_1*244/10
call TryWalkUpperPortal3D
cmp engineMode,MODE_WALK
jne expansion_fail
mov interactTargetId,INTERACT_HELL_TOWER
call TriggerHallInteraction3D
cmp engineMode,MODE_WALK
jne expansion_fail
mov playerY,FP_1*193/10
call ExpansionFrame
cmp upperPortal3D.frame,0
jne expansion_fail
invoke ExpansionSnapshot,OFFSET expansionPortalOff
mov esi,20
expansion_release_half:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_release_half
cmp upperLeverTime3D,280
jne expansion_fail
cmp upperLeverTime3D[4],600
jne expansion_fail
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionLeverRelease
; Reverse the handle halfway through release, without snapping to an endpoint.
mov interactTargetId,INTERACT_UPPER_LEFT
call TriggerHallInteraction3D
cmp upperLeverTime3D,280
jne expansion_fail
cmp upperPortalReady3D,0
jne expansion_fail
mov esi,20
expansion_reenable_half:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_reenable_half
cmp upperPortalReady3D,1
jne expansion_fail
; The right handle is also available after activation, and disables immediately.
mov playerX,FP_1*119/10
mov playerY,FP_1*214/10
call CheckUpperInteractions3D
cmp interactTargetId,INTERACT_UPPER_RIGHT
jne expansion_fail
cmp interactPromptPtr,OFFSET upperRightRelease3D
jne expansion_fail
call TriggerHallInteraction3D
cmp upperLeverMask3D,1
jne expansion_fail
cmp upperPortalReady3D,0
jne expansion_fail
mov esi,40
expansion_release_right:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_release_right
cmp upperLeverTime3D[4],0
jne expansion_fail
cmp upperLeverTime3D,600
jne expansion_fail
mov interactTargetId,INTERACT_UPPER_LEFT
call TriggerHallInteraction3D
mov esi,40
expansion_release_both:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_release_both
cmp upperLeverMask3D,0
jne expansion_fail
cmp upperLeverTime3D,0
jne expansion_fail
cmp upperLeverTime3D[4],0
jne expansion_fail
cmp upperPortalReady3D,0
jne expansion_fail
call CheckUpperInteractions3D
cmp interactTargetId,INTERACT_UPPER_RIGHT
jne expansion_fail
cmp interactPromptPtr,OFFSET upperRightPrompt3D
jne expansion_fail
mov interactTargetId,INTERACT_UPPER_LEFT
call TriggerHallInteraction3D
mov interactTargetId,INTERACT_UPPER_RIGHT
call TriggerHallInteraction3D
mov esi,40
expansion_reopen_both:
add animationClock3D,16
call UpdateUpperEffects3D
dec esi
jnz expansion_reopen_both
cmp upperPortalReady3D,1
jne expansion_fail
mov playerX,FP_1*105/10
mov playerY,FP_1*244/10
call TryWalkUpperPortal3D
cmp engineMode,MODE_HELL_TOWER
jne expansion_fail
call TowerExitToHall
call TryWalkUpperPortal3D
cmp engineMode,MODE_WALK
jne expansion_fail
mov expansionStage,30
mov hallLevel3D,0
mov worldMap3D,OFFSET casinoMap
mov floorTexture3D,100
mov playerX,FP_1*285/10
mov playerY,21*FP_1
mov playerAngle,0
call UpdateInteriorEye3D
call ExpansionFrame
cld
lea esi,screenBuffer
lea edi,expansionSinglePixels
mov ecx,SCREEN_SIZE
rep movsd
invoke CreateEventA,0,0,0,0
mov hRaycastStartEvent,eax
invoke CreateEventA,0,0,0,0
mov hRaycastDoneEvent,eax
mov bRaycastQuit,0
invoke CreateThread,0,65536,OFFSET RaycastWorkerProc,0,0,OFFSET raycastThreadId
mov hRaycastThread,eax
test eax,eax
jz expansion_fail
call ExpansionFrame
lea esi,screenBuffer
lea edi,expansionSinglePixels
mov ecx,SCREEN_SIZE
repe cmpsd
jne expansion_fail
mov bRaycastQuit,1
invoke SetEvent,hRaycastStartEvent
invoke WaitForSingleObject,hRaycastThread,2000
test eax,eax
jnz expansion_fail
invoke CloseHandle,hRaycastThread
invoke CloseHandle,hRaycastStartEvent
invoke CloseHandle,hRaycastDoneEvent
mov hRaycastThread,0
mov hRaycastStartEvent,0
mov hRaycastDoneEvent,0
mov expansionStage,31
mov hallLevel3D,0
mov worldMap3D,OFFSET casinoMap
; Equal walked distance at two update rates must select the same gait phase.
call InitNpcLife3D
invoke NpcPlanPath3D,0,5
test eax,eax
jz expansion_fail
mov npcLife3D.state,NPC_WALK
mov esi,10
expansion_gait_rate16:
invoke NpcMove3D,0,16
dec esi
jnz expansion_gait_rate16
mov eax,casinoNpcs3D.posX
mov expansionGaitX,eax
mov eax,casinoNpcs3D.posY
mov expansionGaitY,eax
mov eax,npcWalkDistance3D
mov expansionGaitPhase,eax
call InitNpcLife3D
invoke NpcPlanPath3D,0,5
test eax,eax
jz expansion_fail
mov npcLife3D.state,NPC_WALK
mov esi,5
expansion_gait_rate32:
invoke NpcMove3D,0,32
dec esi
jnz expansion_gait_rate32
mov eax,casinoNpcs3D.posX
cmp eax,expansionGaitX
jne expansion_fail
mov playerX,eax
mov eax,casinoNpcs3D.posY
cmp eax,expansionGaitY
jne expansion_fail
mov playerY,eax
mov eax,npcWalkDistance3D
cmp eax,expansionGaitPhase
jne expansion_fail
invoke NpcMove3D,0,16
mov eax,npcWalkDistance3D
cmp eax,expansionGaitPhase
jne expansion_fail
cmp npcLife3D.state,NPC_STAND
jne expansion_fail
mov playerX,FP_1*285/10
mov playerY,21*FP_1
call InitNpcLife3D
; A blocked move must not tick the gait. A successful one consumes distance.
mov npcLife3D.state,NPC_WALK
mov npcLife3D.pathCount,0
invoke NpcMove3D,0,16
cmp npcWalkDistance3D,0
jne expansion_fail
invoke NpcPlanPath3D,0,5
test eax,eax
jz expansion_fail
mov npcLife3D.state,NPC_WALK
invoke NpcMove3D,0,16
cmp npcWalkDistance3D,0
je expansion_fail
mov eax,npcWalkDistance3D
mov expansionOcclusionPixel,eax
invoke NpcMove3D,0,0
mov eax,npcWalkDistance3D
cmp eax,expansionOcclusionPixel
jne expansion_fail
; Native renders of each gait phase, not just hashes of offline source art.
mov esi,1
expansion_gait_hide:
imul ebx,esi,SIZEOF NpcLife3D
mov npcLife3D[ebx].state,NPC_AWAY
inc esi
cmp esi,NPC_LIFE_COUNT
jb expansion_gait_hide
mov npcLife3D.state,NPC_WALK
mov npcLife3D.identity,0
mov casinoNpcs3D.posX,FP_1*165/10
mov casinoNpcs3D.posY,FP_1*175/10
mov npcWalkVelocityX3D,FP_1
mov npcWalkVelocityY3D,0
mov playerX,FP_1*165/10
mov playerY,FP_1*195/10
mov playerAngle,384
call UpdateInteriorEye3D
xor esi,esi
expansion_gait_frame:
imul eax,esi,FP_1*3/32
mov npcWalkDistance3D,eax
call UpdateCamera3D
call RenderCeilingAndFloor
call RenderWalls
call PrepareCityDepth3D
call RenderNpcLife3D
mov eax,npcSprite3D.frame
cmp eax,esi
jne expansion_fail
invoke ExpansionSnapshot,expansionGaitPaths[esi*4]
inc esi
cmp esi,8
jb expansion_gait_frame
mov npcWalkVelocityX3D,-FP_1
call PrepareCityDepth3D
call RenderNpcLife3D
cmp npcSprite3D.asset,OFFSET npcWalkLeftArt3D
jne expansion_fail
mov expansionStage,32
mov engineMode,MODE_STAIRS
mov stairRoute3D,1
mov worldMap3D,OFFSET basementStairMap3D
mov floorTexture3D,94
mov playerX,FP_1*46/10
mov playerY,21*FP_1
mov playerAngle,256
mov basementDoorTarget3D,0
mov basementDoorOpen3D,0
mov basementWheelTime3D,0
invoke CanWalk3D,FP_1*39/10,FP_1*227/10
test eax,eax
jnz expansion_fail
mov keys3D[57h],1
mov esi,10
expansion_return_closed:
call ExpansionWalkFrame
dec esi
jnz expansion_return_closed
cmp engineMode,MODE_STAIRS
jne expansion_fail
cmp playerX,FP_1*405/100
jb expansion_fail
cmp basementDoorTarget3D,FP_1
jne expansion_fail
cmp basementDoorOpen3D,0
jne expansion_fail
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionDoorBack
mov esi,150
expansion_return_auto_open:
call ExpansionWalkFrame
cmp engineMode,MODE_WALK
je expansion_return_opened
dec esi
jnz expansion_return_auto_open
jmp expansion_fail
expansion_return_opened:
mov keys3D[57h],0
cmp hallLevel3D,0
jne expansion_fail
cmp basementWheelTime3D,700
jne expansion_fail
cmp basementDoorOpen3D,FP_1*9/10
jb expansion_fail
mov engineMode,MODE_STAIRS
mov stairRoute3D,1
mov worldMap3D,OFFSET basementStairMap3D
invoke CanWalk3D,FP_1*39/10,FP_1*227/10
test eax,eax
jnz expansion_fail
invoke CanWalk3D,FP_1*39/10,21*FP_1
cmp eax,1
jne expansion_fail
mov engineMode,MODE_WALK
mov worldMap3D,OFFSET casinoMap
mov expansionStage,33
mov playerX,FP_1*305/10
mov playerY,FP_1*305/10
call InitNpcLife3D
mov deltaTime3D,50
mov esi,2400
expansion_long_gait:
call UpdateNpcLife3D
xor edi,edi
expansion_gait_bounds:
cmp npcWalkDistance3D[edi*4],FP_1*3/4
jae expansion_fail
imul ebx,edi,SIZEOF NpcMindEntity
invoke CanWalk3D,casinoNpcs3D[ebx].posX,casinoNpcs3D[ebx].posY
cmp eax,1
jne expansion_fail
inc edi
cmp edi,NPC_LIFE_COUNT
jb expansion_gait_bounds
dec esi
jnz expansion_long_gait
cmp npcLifeClock3D,120000
jne expansion_fail
mov expansionStage,34
mov deltaTime3D,16
mov floorTexture3D,100
mov playerX,FP_1*288/10
mov playerY,FP_1*105/10
mov playerAngle,0
mov basementDoorTarget3D,FP_1
mov basementDoorOpen3D,FP_1
mov basementWheelTime3D,700
mov basementDoorHold3D,0
invoke ExpansionDoorTicks,200
cmp basementDoorOpen3D,FP_1
jne expansion_fail
cmp basementDoorHold3D,1200
jne expansion_fail
mov engineMode,MODE_STAIRS
mov stairRoute3D,1
mov worldMap3D,OFFSET basementStairMap3D
mov playerX,FP_1*43/10
mov playerY,21*FP_1
invoke ExpansionDoorTicks,200
cmp basementDoorOpen3D,FP_1
jne expansion_fail
cmp basementWheelTime3D,700
jne expansion_fail
mov engineMode,MODE_WALK
mov worldMap3D,OFFSET casinoMap
mov playerX,25*FP_1
mov playerY,FP_1*105/10
add animationClock3D,16
call UpdateBasementDoor3D
cmp basementDoorHold3D,1184
jne expansion_fail
call UpdateBasementDoor3D
cmp basementDoorHold3D,1184
jne expansion_fail
invoke ExpansionDoorTicks,74
cmp basementDoorHold3D,0
jne expansion_fail
cmp basementDoorOpen3D,FP_1
jne expansion_fail
invoke ExpansionDoorTicks,20
cmp basementDoorOpen3D,0
je expansion_fail
cmp basementDoorOpen3D,FP_1
jae expansion_fail
cmp basementWheelTime3D,700
jne expansion_fail
mov eax,basementDoorOpen3D
mov expansionDoorState,eax
mov playerX,27*FP_1
call UpdateInteriorEye3D
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionDoorClosing
; Returning during the swing reverses smoothly and keeps the wheel unlocked.
invoke ExpansionDoorTicks,1
mov eax,basementDoorOpen3D
cmp eax,expansionDoorState
jbe expansion_fail
cmp eax,FP_1
jae expansion_fail
cmp basementWheelTime3D,700
jne expansion_fail
invoke ExpansionDoorTicks,75
cmp basementDoorOpen3D,FP_1
jne expansion_fail
mov playerX,25*FP_1
invoke ExpansionDoorTicks,75
mov esi,70
expansion_wait_closed_leaf:
invoke ExpansionDoorTicks,1
cmp basementDoorOpen3D,0
je expansion_leaf_shut
cmp basementWheelTime3D,700
jne expansion_fail
dec esi
jnz expansion_wait_closed_leaf
jmp expansion_fail
expansion_leaf_shut:
cmp basementWheelTime3D,600
jbe expansion_fail
invoke ExpansionDoorTicks,20
cmp basementDoorOpen3D,0
jne expansion_fail
cmp basementWheelTime3D,0
je expansion_fail
cmp basementWheelTime3D,700
jae expansion_fail
mov eax,basementWheelTime3D
mov expansionDoorState,eax
mov playerX,27*FP_1
call ExpansionFrame
invoke ExpansionSnapshot,OFFSET expansionDoorLocking
; Returning during locking unlocks from that pose before moving the leaf.
invoke ExpansionDoorTicks,1
cmp basementDoorOpen3D,0
jne expansion_fail
mov eax,basementWheelTime3D
cmp eax,expansionDoorState
jbe expansion_fail
invoke ExpansionDoorTicks,100
cmp basementDoorOpen3D,FP_1
jne expansion_fail
mov playerX,25*FP_1
invoke ExpansionDoorTicks,200
cmp basementDoorTarget3D,0
jne expansion_fail
cmp basementDoorOpen3D,0
jne expansion_fail
cmp basementWheelTime3D,0
jne expansion_fail
invoke CanWalk3D,FP_1*289/10,FP_1*105/10
test eax,eax
jnz expansion_fail
mov playerX,27*FP_1
call ExpansionFrame
cmp portalPixelsWritten3D,0
jne expansion_fail
invoke ExpansionSnapshot,OFFSET expansionDoorShut
mov engineMode,MODE_STAIRS
mov stairRoute3D,1
mov worldMap3D,OFFSET basementStairMap3D
mov playerX,FP_1*43/10
mov playerY,21*FP_1
invoke ExpansionDoorTicks,120
cmp basementDoorOpen3D,FP_1
jne expansion_fail
mov playerX,8*FP_1
invoke ExpansionDoorTicks,200
cmp basementDoorOpen3D,0
jne expansion_fail
cmp basementWheelTime3D,0
jne expansion_fail
invoke CanWalk3D,FP_1*39/10,21*FP_1
test eax,eax
jnz expansion_fail
mov playerX,FP_1*43/10
invoke ExpansionDoorTicks,120
cmp basementDoorOpen3D,FP_1
jne expansion_fail
; Optional E also works outside the automatic opening radius, then expires.
mov engineMode,MODE_WALK
mov worldMap3D,OFFSET casinoMap
mov playerX,25*FP_1
mov playerY,FP_1*105/10
mov basementDoorOpen3D,0
mov basementWheelTime3D,0
mov basementDoorHold3D,0
mov interactTargetId,INTERACT_BASEMENT
call TriggerHallInteraction3D
invoke ExpansionDoorTicks,110
cmp basementDoorOpen3D,FP_1
jne expansion_fail
invoke ExpansionDoorTicks,200
cmp basementDoorOpen3D,0
jne expansion_fail
cmp basementWheelTime3D,0
jne expansion_fail
call FortuneNativeTests
call BarCameraNativeTests
invoke ExitProcess,0
expansion_fail:
invoke ExitProcess,expansionStage
ret
ExpansionTestStart ENDP
include tools/tests/fortune_room_test.inc
include tools/tests/bar_camera_test.inc