RoofTestCrashFilter PROTO :DWORD .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 cmp hallLevel3D,HALL_ROOF je expansion_world_ready cmp hallLevel3D,HALL_BOWLING je expansion_world_ready call RenderCeilingAndFloor call RenderWalls call PrepareCityDepth3D call RenderSprites expansion_world_ready: 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 invoke SetUnhandledExceptionFilter,OFFSET RoofTestCrashFilter call InitPaths3D call LoadTextures3D call InitializeCasinoSprites3D call InitHall3D call LoadHallAssets3D cmp characterCast3D.pixels,0 je expansion_fail cmp characterCast3D.frames,CAST_COUNT*CAST_STRIDE 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 npcRise3D,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 sub eax,40 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 characterCast3D jne expansion_fail cmp npcSprite3D.frame,55 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 call FortuneDialogNativeTests call ExpansionDartsTests call RoofNativeTests call BowlingNativeTests 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 include tools/tests/fortune_dialog_test.inc include tools/tests/darts_hall_test.inc include tools/tests/rooftop_test.inc include tools/tests/bowling_test.inc include tools/tests/character_cast_test.inc