.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 call FortuneDialogNativeTests call ExpansionDartsTests 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