; Room planes use independent ceiling/floor distances; the eye is not the room midpoint. RenderCeilingAndFloor PROC uses ebx esi edi local row:DWORD,distance:DWORD,sx:DWORD,sy:DWORD,wx:DWORD,wy:DWORD local material:DWORD,shade:DWORD mov row, 0 hall_plane_row: mov eax, row cmp eax, viewHorizon3D jge hall_plane_floor cmp outdoorRender3D, 0 jne hall_plane_next_row mov ecx, viewHorizon3D sub ecx, eax mov eax, viewCeiling3D sub eax, viewEye3D mov material, 101 cmp hallLevel3D, 0 je @F mov material, 93 @@: jmp hall_plane_distance hall_plane_floor: sub eax, viewHorizon3D inc eax mov ecx, eax mov eax, viewEye3D mov ebx, floorTexture3D mov material, ebx hall_plane_distance: imul eax, SCREEN_H cdq idiv ecx mov distance, eax shr eax, 14 neg eax add eax, 256 cmp eax, 55 jge @F mov eax, 55 @@: mov shade, eax cmp hallLevel3D, 0 je @F mov eax, shade imul eax, 7 shr eax, 3 mov shade, eax @@: mov eax, playerPlaneX shl eax, 1 imul distance shrd eax, edx, 16 cdq mov ecx, SCREEN_W idiv ecx mov sx, eax mov eax, playerPlaneY shl eax, 1 imul distance shrd eax, edx, 16 cdq idiv ecx mov sy, eax mov eax, playerDirX sub eax, playerPlaneX imul distance shrd eax, edx, 16 add eax, playerX mov wx, eax mov eax, playerDirY sub eax, playerPlaneY imul distance shrd eax, edx, 16 add eax, playerY mov wy, eax mov eax, row imul eax, SCREEN_W*4 lea edi, screenBuffer[eax] mov esi, SCREEN_W hall_plane_pixel: mov ebx, material cmp outdoorRender3D, 0 je hall_plane_sample mov eax, wy cmp eax, 13*FP_1 jb hall_plane_crossroad cmp eax, 16*FP_1 jb hall_plane_sample hall_plane_crossroad: mov eax, wx cmp eax, 29*FP_1 jl hall_plane_sidewalk cmp eax, 32*FP_1 jl hall_plane_sample hall_plane_sidewalk: mov ebx, 72 hall_plane_sample: cmp outdoorRender3D, 0 jne hall_plane_low_sample mov eax, row cmp eax, viewHorizon3D jl hall_plane_choose_hd cmp hallLevel3D, 0 jne hall_plane_choose_hd cmp wy, 23*FP_1 jl hall_plane_choose_hd cmp wx, 12*FP_1 jl hall_plane_wood cmp wx, 23*FP_1 jl hall_plane_choose_hd hall_plane_wood: mov ebx, 85 hall_plane_choose_hd: cmp distance, 6*FP_1 ja hall_plane_low_sample mov ecx, hallRoomHD3D.pixels cmp ebx, 80 jb hall_plane_low_sample cmp ebx, 103 ja hall_plane_low_sample cmp ebx, 92 jb hall_plane_old_hd mov ecx, hallArchitectureHD3D.pixels sub ebx, 12 hall_plane_old_hd: test ecx, ecx jz hall_plane_hd_fallback sub ebx, 80 shl ebx, 18 add ecx, ebx mov eax, wy shr eax, 8 and eax, 255 shl eax, 8 mov edx, wx shr edx, 8 and edx, 255 add eax, edx mov eax, [ecx+eax*4] jmp hall_plane_shade hall_plane_hd_fallback: mov ebx, material hall_plane_low_sample: shl ebx, 16 mov eax, wy shr eax, 9 and eax, 127 shl eax, 7 mov edx, wx shr edx, 9 and edx, 127 add eax, edx lea ebx, texturesBuffer[ebx] mov eax, [ebx+eax*4] hall_plane_shade: cmp outdoorRender3D, 0 je hall_plane_ambient invoke ShadeCityGround3D, eax, wx, wy, shade jmp hall_plane_store hall_plane_ambient: mov ebx, eax and eax, 0000FF00h imul eax, shade shr eax, 8 and eax, 0000FF00h mov ecx, ebx and ecx, 0FFh imul ecx, shade shr ecx, 8 or eax, ecx shr ebx, 16 and ebx, 0FFh imul ebx, shade shr ebx, 8 shl ebx, 16 or eax, ebx hall_plane_store: stosd mov eax, sx add wx, eax mov eax, sy add wy, eax dec esi jnz hall_plane_pixel hall_plane_next_row: inc row cmp row, SCREEN_H jb hall_plane_row ret RenderCeilingAndFloor ENDP RenderSprites PROC uses ebx esi edi cmp hallLevel3D, 0 jne hall_sprites_done xor esi, esi hall_sprite_loop: cmp esi, 14 je hall_sprite_next imul ebx, esi, SIZEOF SpriteObject mov eax, casinoSprites[ebx].posX mov hallSpriteTemp3D.posX, eax mov eax, casinoSprites[ebx].posY mov hallSpriteTemp3D.posY, eax mov eax, hallSpriteWidths3D[esi*4] mov hallSpriteTemp3D.worldW, eax mov eax, hallSpriteHeights3D[esi*4] mov hallSpriteTemp3D.worldH, eax mov hallSpriteTemp3D.groundZ, 0 cmp esi, 3 jae @F mov hallSpriteTemp3D.groundZ, FP_1/8 @@: mov ecx, casinoSprites[ebx].animSpeed imul ecx, 40 test ecx, ecx jnz @F mov ecx, 40 @@: mov eax, animationClock3D cmp esi,3 jae @F mov edx,casinoSprites[ebx].curFrame imul edx,ecx add eax,edx @@: xor edx, edx div ecx mov ecx, casinoSprites[ebx].frameCount test ecx, ecx jnz @F mov ecx, 1 @@: xor edx, edx div ecx add edx, casinoSprites[ebx].baseTexId shl edx, 16 add edx, OFFSET texturesBuffer mov hallLegacyPack3D.pixels, edx mov hallSpriteTemp3D.frame, 0 mov hallSpriteTemp3D.asset, OFFSET hallLegacyPack3D invoke DrawCityObject3D, OFFSET hallSpriteTemp3D, 0 hall_sprite_next: inc esi cmp esi, NUM_SPRITES jb hall_sprite_loop hall_sprites_done: ret RenderSprites ENDP InitHall3D PROC uses esi edi lea esi, hallLayout3D lea edi, casinoMap mov ecx, 256 rep movsd mov hallLevel3D, 0 mov worldMap3D, OFFSET casinoMap mov viewEye3D, FP_1*17/20 mov viewCeiling3D, FP_1*5/2 mov floorTexture3D, 100 mov casinoSprites[14*SIZEOF SpriteObject].interactId, 0 mov patronClock3D, 0 ret InitHall3D ENDP LoadHallAssets3D PROC invoke LoadAnimPack3D, OFFSET hallBartender3D, OFFSET hallBartenderPath3D invoke LoadAnimPack3D, OFFSET hallProps3D, OFFSET hallPropsPath3D invoke LoadAnimPack3D, OFFSET hallDrink3D, OFFSET hallDrinkPath3D invoke LoadAnimPack3D, OFFSET hallCityDetail3D, OFFSET hallCityDetailPath3D invoke LoadAnimPack3D, OFFSET hallRoomMaterials3D, OFFSET hallRoomMaterialsPath3D invoke LoadAnimPack3D, OFFSET hallPatrons3D, OFFSET hallPatronsPath3D invoke LoadAnimPack3D, OFFSET hallStreetLife3D, OFFSET hallStreetLifePath3D invoke LoadAnimPack3D, OFFSET hallRoomHD3D, OFFSET hallRoomHDPath3D invoke LoadAnimPack3D, OFFSET hallArchitecture3D, OFFSET hallArchitecturePath3D invoke LoadAnimPack3D, OFFSET hallArchitectureHD3D, OFFSET hallArchitectureHDPath3D invoke LoadAnimPack3D, OFFSET hallDecorPack3D, OFFSET hallDecorPath3D cmp hallArchitecture3D.pixels, 0 je @F push esi push edi mov esi, hallArchitecture3D.pixels mov edi, OFFSET texturesBuffer+(92*128*128*4) mov ecx, 12*128*128 rep movsd pop edi pop esi @@: cmp hallRoomMaterials3D.pixels, 0 je @F push esi push edi mov esi, hallRoomMaterials3D.pixels mov edi, OFFSET texturesBuffer+(80*128*128*4) mov ecx, 12*128*128 rep movsd pop edi pop esi @@: invoke LoadAnimPack3D, OFFSET materialsPack3D, OFFSET materialsPath3D cmp materialsPack3D.pixels, 0 je hall_load_sign push ebx push esi push edi xor ebx, ebx hall_copy_material: mov esi, materialsPack3D.pixels mov eax, ebx shl eax, 16 add esi, eax mov eax, hallMaterialSlots3D[ebx*4] shl eax, 16 lea edi, texturesBuffer[eax] mov ecx, 128*128 rep movsd inc ebx cmp ebx, 4 jb hall_copy_material pop edi pop esi pop ebx hall_load_sign: ret LoadHallAssets3D ENDP FreeHallAssets3D PROC invoke FreeAnimPack3D, OFFSET hallBartender3D invoke FreeAnimPack3D, OFFSET hallProps3D invoke FreeAnimPack3D, OFFSET hallDrink3D invoke FreeAnimPack3D, OFFSET hallCityDetail3D invoke FreeAnimPack3D, OFFSET hallRoomMaterials3D invoke FreeAnimPack3D, OFFSET hallPatrons3D invoke FreeAnimPack3D, OFFSET hallStreetLife3D invoke FreeAnimPack3D, OFFSET hallRoomHD3D invoke FreeAnimPack3D, OFFSET hallArchitecture3D invoke FreeAnimPack3D, OFFSET hallArchitectureHD3D invoke FreeAnimPack3D, OFFSET hallDecorPack3D ret FreeHallAssets3D ENDP ; EAX=pixels, ECX=width, EDX=height; legacy materials keep their 128px fallback. ResolveWorldSurface3D PROC uses esi material:DWORD mov eax, material cmp eax,110 jne @F mov eax,OFFSET cityBenchMetal3D mov ecx,32 mov edx,32 ret @@: cmp eax, 104 jae surface_decor cmp eax, 92 jae surface_architecture cmp eax, 80 jae surface_room cmp eax, 57 jb surface_legacy cmp eax, 72 ja surface_legacy mov esi, OFFSET hallCityDetail3D sub eax, 57 jmp surface_pack surface_room: mov esi, OFFSET hallRoomHD3D sub eax, 80 jmp surface_pack surface_architecture: mov esi, OFFSET hallArchitectureHD3D sub eax, 92 jmp surface_pack surface_decor: cmp eax, 109 ja surface_missing mov esi, OFFSET hallDecorPack3D sub eax, 104 surface_pack: cmp [esi].AnimPack3D.pixels, 0 je surface_legacy cmp eax, [esi].AnimPack3D.frames jae surface_missing mov ecx, [esi].AnimPack3D.imageWidth mov edx, [esi].AnimPack3D.height imul eax, ecx imul eax, edx shl eax, 2 add eax, [esi].AnimPack3D.pixels ret surface_legacy: mov eax, material cmp eax, 104 jae surface_missing shl eax, 16 add eax, OFFSET texturesBuffer mov ecx, 128 mov edx, 128 ret surface_missing: xor eax, eax mov ecx, 128 mov edx, 128 ret ResolveWorldSurface3D ENDP RenderHall3D PROC uses ebx esi cmp hallLevel3D, 0 jne hall_render_bunker ; Fixed bar counter/stage are world geometry, with height and occlusion. invoke DrawCityBoxSides3D, 3*FP_1,9*FP_1,23*FP_1,24*FP_1,0,FP_1*13/20,86 invoke DrawCityHorizontal3D, 3*FP_1,9*FP_1,23*FP_1,24*FP_1,FP_1*13/20,85 ; Three physical panels preserve detail instead of stretching one over the whole bar. invoke DrawCityVertical3D,1,24*FP_1,3*FP_1,5*FP_1,0,FP_1*13/20,84 invoke DrawCityVertical3D,1,24*FP_1,5*FP_1,7*FP_1,0,FP_1*13/20,84 invoke DrawCityVertical3D,1,24*FP_1,7*FP_1,9*FP_1,0,FP_1*13/20,84 invoke DrawCityBoxSides3D,3*FP_1,9*FP_1,21*FP_1,22*FP_1,0,FP_1*19/10,86 invoke DrawCityVertical3D,1,22*FP_1,3*FP_1,5*FP_1,FP_1*3/5,FP_1*19/10,87 invoke DrawCityVertical3D,1,22*FP_1,5*FP_1,7*FP_1,FP_1*3/5,FP_1*19/10,87 invoke DrawCityVertical3D,1,22*FP_1,7*FP_1,9*FP_1,FP_1*3/5,FP_1*19/10,87 invoke DrawCityBoxSides3D, 10*FP_1,22*FP_1,2*FP_1,5*FP_1,0,FP_1/8,2 invoke DrawCityHorizontal3D, 10*FP_1,22*FP_1,2*FP_1,5*FP_1,FP_1/8,2 ; Cabaret has a solid stage, side wings, stairs and lighting truss. invoke DrawCityVertical3D,0,10*FP_1,2*FP_1,5*FP_1,FP_1/8,FP_1*23/10,2 invoke DrawCityVertical3D,0,22*FP_1,2*FP_1,5*FP_1,FP_1/8,FP_1*23/10,2 invoke DrawCityVertical3D,1,2*FP_1,10*FP_1,22*FP_1,FP_1/8,FP_1*23/10,2 invoke DrawCityHorizontal3D,14*FP_1,18*FP_1,5*FP_1,FP_1*11/2,FP_1/12,85 invoke DrawCityHorizontal3D,14*FP_1,18*FP_1,FP_1*11/2,6*FP_1,FP_1/24,85 invoke DrawCityBoxSides3D,10*FP_1,22*FP_1,5*FP_1,FP_1*51/10,FP_1*11/5,FP_1*23/10,86 ; Low room boundaries frame dining and billiards without closing their entrances. invoke DrawCityBoxSides3D,FP_1*119/10,FP_1*121/10,24*FP_1,FP_1*51/2,0,FP_1*13/20,102 invoke DrawCityHorizontal3D,FP_1*119/10,FP_1*121/10,24*FP_1,FP_1*51/2,FP_1*13/20,85 invoke DrawCityBoxSides3D,FP_1*119/10,FP_1*121/10,FP_1*55/2,30*FP_1,0,FP_1*13/20,102 invoke DrawCityBoxSides3D,23*FP_1,FP_1*116/5,23*FP_1,24*FP_1,0,FP_1*13/20,102 invoke DrawCityBoxSides3D,23*FP_1,FP_1*116/5,26*FP_1,29*FP_1,0,FP_1*13/20,102 invoke DrawCityVertical3D,1,6*FP_1,14*FP_1,18*FP_1,FP_1*17/10,FP_1*12/5,109 invoke DrawCityBoxSides3D,1*FP_1,9*FP_1,FP_1,FP_1*6/5,FP_1*11/5,FP_1*5/2,103 invoke DrawCityBoxSides3D,23*FP_1,31*FP_1,FP_1,FP_1*6/5,FP_1*11/5,FP_1*5/2,103 mov eax, animationClock3D xor edx, edx mov ecx, 220 div ecx and eax, 1 cmp engineMode, MODE_BAR jne hall_bartender_pose cmp barPhase3D, 1 jne hall_bartender_pose mov eax, barTime3D xor edx, edx mov ecx, 375 div ecx cmp eax, 7 jbe hall_bartender_pose mov eax, 7 jmp hall_bartender_selected hall_bartender_pose: cmp engineMode, MODE_BAR jne hall_bartender_store cmp barPhase3D, 1 je hall_bartender_selected cmp barPhase3D, 2 jne hall_bartender_store mov eax,7 hall_bartender_selected: mov ecx, barSelection3D shl ecx, 3 add eax, ecx hall_bartender_store: mov hallObjects3D[4*SIZEOF CityProp3D].frame, eax xor esi, esi hall_render_props: ; The former whole-stage billboard is replaced with world surfaces above. cmp esi, 15 je hall_render_next_prop imul eax, esi, SIZEOF CityProp3D add eax, OFFSET hallObjects3D invoke DrawCityObject3D, eax, 0 hall_render_next_prop: inc esi cmp esi, HALL_PROP_COUNT jb hall_render_props xor esi, esi hall_render_lights: imul eax, esi, SIZEOF CityProp3D add eax, OFFSET hallLights3D invoke DrawCityObject3D,eax,0 inc esi cmp esi, HALL_LIGHT_COUNT jb hall_render_lights call RenderPatrons3D ret hall_render_bunker: mov esi, 0 hall_stair_step: mov eax, esi imul eax, FP_1/2 add eax, 24*FP_1 lea ebx, [eax+FP_1/2] lea ecx, [esi+1] imul ecx, FP_1/8 invoke DrawCityHorizontal3D,15*FP_1,18*FP_1,eax,ebx,ecx,88 mov eax, esi imul eax, FP_1/2 add eax, 24*FP_1 mov ecx, esi imul ecx, FP_1/8 lea edx, [ecx+FP_1/8] invoke DrawCityVertical3D,1,eax,15*FP_1,18*FP_1,ecx,edx,92 inc esi cmp esi, 8 jb hall_stair_step invoke DrawCityBoxSides3D, 14*FP_1,20*FP_1,14*FP_1,15*FP_1,0,FP_1/2,96 invoke DrawCityHorizontal3D, 14*FP_1,20*FP_1,14*FP_1,15*FP_1,FP_1/2,97 invoke DrawCityVertical3D,1,11*FP_1+FP_1/10,14*FP_1,16*FP_1,FP_1/4,FP_1*8/5,95 invoke DrawCityVertical3D,1,11*FP_1+FP_1/10,17*FP_1,19*FP_1+FP_HALF,FP_1/4,FP_1*8/5,95 invoke DrawCityVertical3D,0,19*FP_1+FP_1*9/10,17*FP_1,20*FP_1,0,FP_1*3/2,99 ; A narrow, stepped vault is real geometry, not a square workshop ceiling. invoke DrawCityHorizontal3D,14*FP_1,15*FP_1,11*FP_1,24*FP_1,FP_1*9/5,93 invoke DrawCityVertical3D,0,15*FP_1,11*FP_1,24*FP_1,FP_1*9/5,FP_1*11/5,93 invoke DrawCityHorizontal3D,15*FP_1,16*FP_1,11*FP_1,24*FP_1,FP_1*11/5,93 invoke DrawCityVertical3D,0,16*FP_1,11*FP_1,24*FP_1,FP_1*11/5,FP_1*5/2,93 invoke DrawCityHorizontal3D,18*FP_1,19*FP_1,11*FP_1,24*FP_1,FP_1*11/5,93 invoke DrawCityVertical3D,0,18*FP_1,11*FP_1,24*FP_1,FP_1*11/5,FP_1*5/2,93 invoke DrawCityHorizontal3D,19*FP_1,20*FP_1,11*FP_1,24*FP_1,FP_1*9/5,93 invoke DrawCityVertical3D,0,19*FP_1,11*FP_1,24*FP_1,FP_1*9/5,FP_1*11/5,93 mov eax, animationClock3D xor edx, edx mov ecx, 240 div ecx and eax, 7 mov bunkerMerchant3D.frame, eax invoke DrawCityObject3D, OFFSET bunkerMerchant3D, 0 invoke DrawCityObject3D, OFFSET bunkerDisplay3D, 0 invoke DrawCityObject3D, OFFSET bunkerDesk3D, 0 invoke DrawCityObject3D, OFFSET bunkerDoor3D, 0 ret RenderHall3D ENDP ; Extra rooms use the same distance and facing checks as the existing tables. HallTarget3D PROC uses ebx wx:DWORD,wy:DWORD,id:DWORD,prompt:DWORD mov eax, wx sub eax, playerX sar eax, 8 mov ebx, eax imul eax, eax mov ecx, eax mov eax, wy sub eax, playerY sar eax, 8 mov edx, eax imul eax, eax add ecx, eax cmp ecx, 262144 jae hall_target_done mov eax, playerDirX sar eax, 8 imul eax, ebx mov ebx, playerDirY sar ebx, 8 imul ebx, edx add eax, ebx cmp eax, 18000 jl hall_target_done mov eax, id mov interactTargetId, eax mov eax, prompt mov interactPromptPtr, eax hall_target_done: ret HallTarget3D ENDP CheckHallInteractions3D PROC cmp hallLevel3D, 0 jne hall_check_bunker cmp playerX, 3*FP_1 jl hall_check_other cmp playerX, 9*FP_1 jg hall_check_other cmp playerY, 24*FP_1+9830 jl hall_check_other ; A point on the serving edge remains in front even when standing close. invoke HallTarget3D, playerX,23*FP_1+FP_HALF,INTERACT_BAR,OFFSET hallBarPrompt3D hall_check_other: invoke HallTarget3D, 28*FP_1+FP_HALF,10*FP_1+FP_HALF,INTERACT_BASEMENT,OFFSET hallBunkerPrompt3D invoke HallTarget3D, 27*FP_1,25*FP_1,INTERACT_BILLIARDS,OFFSET hallPoolPrompt3D ret hall_check_bunker: mov interactTargetId, 0 mov interactPromptPtr, 0 invoke HallTarget3D, 16*FP_1,14*FP_1+FP_HALF,INTERACT_SHOP,OFFSET promptShop invoke HallTarget3D, 16*FP_1,27*FP_1,INTERACT_UPSTAIRS,OFFSET hallUpPrompt3D ret CheckHallInteractions3D ENDP TriggerHallInteraction3D PROC cmp interactTargetId, INTERACT_BAR jne hall_trigger_stairs call LoadHallAssets3D mov engineMode, MODE_BAR mov barPhase3D, 0 mov barTime3D, 0 call ClearKeys3D mov eax, 1 ret hall_trigger_stairs: cmp interactTargetId, INTERACT_BASEMENT je hall_begin_stairs cmp interactTargetId, INTERACT_UPSTAIRS jne hall_trigger_none hall_begin_stairs: cmp hallLevel3D, 0 jne @F mov eax, playerX mov stairsReturnX3D, eax mov eax, playerY mov stairsReturnY3D, eax mov eax, playerAngle mov stairsReturnAngle3D, eax @@: mov engineMode, MODE_STAIRS mov stairsTime3D, 0 call ClearKeys3D mov eax, 1 ret hall_trigger_none: cmp interactTargetId, INTERACT_BILLIARDS jne @F call InitPool3D mov engineMode, MODE_POOL call ClearKeys3D mov eax, 1 ret @@: xor eax, eax ret TriggerHallInteraction3D ENDP HandleStairs3D PROC mov eax, deltaTime3D add stairsTime3D, eax cmp stairsTime3D, 1600 jb stairs_handle_done xor hallLevel3D, 1 cmp hallLevel3D, 0 je stairs_back_to_hall mov worldMap3D, OFFSET bunkerMap3D mov floorTexture3D, 94 mov viewCeiling3D, FP_1*5/2 mov playerX, 16*FP_1+FP_HALF mov playerY, 23*FP_1+FP_HALF mov playerAngle, 384 jmp stairs_complete stairs_back_to_hall: mov worldMap3D, OFFSET casinoMap mov floorTexture3D, 100 mov viewCeiling3D, FP_1*5/2 mov eax, stairsReturnX3D mov playerX, eax mov eax, stairsReturnY3D mov playerY, eax mov eax, stairsReturnAngle3D mov playerAngle, eax stairs_complete: mov viewEye3D, FP_1*17/20 mov engineMode, MODE_WALK call ClearKeys3D stairs_handle_done: ret HandleStairs3D ENDP RenderStairs3D PROC uses ebx esi local oldX:DWORD,oldY:DWORD,oldAngle:DWORD,oldMap:DWORD,oldEye:DWORD,oldCeiling:DWORD,oldLevel:DWORD,oldFloor:DWORD mov eax, playerX mov oldX, eax mov eax, playerY mov oldY, eax mov eax, playerAngle mov oldAngle, eax mov eax, worldMap3D mov oldMap, eax mov eax, viewEye3D mov oldEye, eax mov eax, viewCeiling3D mov oldCeiling, eax mov eax, hallLevel3D mov oldLevel, eax mov eax, floorTexture3D mov oldFloor, eax mov hallLevel3D, 1 mov worldMap3D, OFFSET bunkerMap3D mov floorTexture3D, 94 mov viewCeiling3D, FP_1*5/2 mov playerX, 16*FP_1+FP_HALF mov playerAngle, 384 mov eax, stairsTime3D cmp eax, 1600 jbe @F mov eax, 1600 @@: cmp oldLevel, 0 je @F neg eax add eax, 1600 @@: imul eax, 4*FP_1 xor edx, edx mov ecx, 1600 div ecx mov ebx, 27*FP_1+FP_HALF sub ebx, eax mov playerY, ebx shr eax, 2 neg eax add eax, FP_1*37/20 mov viewEye3D, eax call UpdateCamera3D call RenderCeilingAndFloor call RenderWalls call PrepareCityDepth3D call RenderHall3D mov eax, oldX mov playerX, eax mov eax, oldY mov playerY, eax mov eax, oldAngle mov playerAngle, eax mov eax, oldMap mov worldMap3D, eax mov eax, oldEye mov viewEye3D, eax mov eax, oldCeiling mov viewCeiling3D, eax mov eax, oldLevel mov hallLevel3D, eax mov eax, oldFloor mov floorTexture3D, eax call UpdateCamera3D ret RenderStairs3D ENDP BuyBarDrink3D PROC drink:DWORD cmp barPhase3D, 0 jne bar_buy_done mov ecx, drink cmp ecx, 2 ja bar_buy_done mov eax, barPrice3D[ecx*4] cmp bankroll, eax jb bar_buy_poor sub bankroll, eax mov barSelection3D, ecx mov barPhase3D, 1 mov barTime3D, 0 call SaveProgress3D ret bar_buy_poor: invoke CopyString3D, OFFSET barPoor3D, OFFSET flashMessage mov flashTimer, 150 bar_buy_done: ret BuyBarDrink3D ENDP HandleBar3D PROC uses ebx cmp barPhase3D, 0 jne bar_animate mov ebx, 31h bar_select_key: invoke Pressed3D, ebx test eax, eax jz @F mov eax, ebx sub eax, 31h invoke BuyBarDrink3D, eax ret @@: inc ebx cmp ebx, 33h jbe bar_select_key cmp mouseClick3D, 0 je bar_handle_done cmp mouseX3D, 170 jb bar_handle_done cmp mouseY3D, 98 jb bar_handle_done cmp mouseY3D, 155 jae bar_handle_done mov eax, mouseY3D sub eax, 98 xor edx, edx mov ecx, 19 div ecx invoke BuyBarDrink3D, eax ret bar_animate: mov eax, deltaTime3D add barTime3D, eax cmp barPhase3D, 1 jne bar_after_drink cmp barTime3D, 3000 jb bar_handle_done mov barPhase3D, 2 mov barTime3D, 0 ret bar_after_drink: cmp barTime3D, 2400 jb bar_handle_done mov ecx, barSelection3D mov eax, barStrength3D[ecx*4] test eax, eax jnz @F mov barIntoxication3D, 0 @@: add barIntoxication3D, eax cmp barIntoxication3D, 60000 jbe @F mov barIntoxication3D, 60000 @@: mov barPhase3D, 0 mov barTime3D, 0 bar_handle_done: ret HandleBar3D ENDP RenderBar3D PROC uses ebx local oldX:DWORD,oldY:DWORD,oldAngle:DWORD,oldHorizon:DWORD,oldEye:DWORD,oldMode:DWORD mov eax, playerX mov oldX, eax mov eax, playerY mov oldY, eax mov eax, playerAngle mov oldAngle, eax mov eax, viewHorizon3D mov oldHorizon, eax mov eax, viewEye3D mov oldEye, eax mov eax, engineMode mov oldMode, eax mov engineMode, MODE_BAR mov playerX, 6*FP_1 mov playerY, 25*FP_1+FP_1/3 mov playerAngle, 384 mov viewHorizon3D, HALF_H mov viewEye3D, FP_1*17/20 call UpdateCamera3D call RenderCeilingAndFloor call RenderWalls call PrepareCityDepth3D call RenderHall3D cmp barPhase3D, 0 jne bar_active_scene invoke DrawBoxBorder, 170,83,146,86,0017201Bh,00836D42h,1 invoke DrawString8x8, 174,99,OFFSET barDrinks3D,00E4D9BDh invoke DrawString8x8, 174,118,OFFSET barDrinks23D,00E4D9BDh invoke DrawString8x8, 174,137,OFFSET barDrinks33D,00BCD7D0h jmp bar_draw_title bar_active_scene: mov ebx, OFFSET barMixText3D cmp barPhase3D, 2 jne @F mov ebx, OFFSET barDrinkText3D @@: invoke DrawBox,170,83,146,24,0017201Bh invoke DrawString8x8,176,91,ebx,00E7CA88h bar_draw_title: invoke DrawBox,0,0,320,28,0014191Dh invoke DrawString8x8, 18,15,OFFSET barName3D,00E7CA88h cmp barPhase3D, 2 jne bar_render_done mov eax, barTime3D xor edx, edx mov ecx, 343 div ecx cmp eax, 6 jbe @F mov eax, 6 @@: mov ecx, barSelection3D imul ecx, 7 add eax, ecx invoke DrawAnimSprite3D, OFFSET hallDrink3D,eax,272,170,360,360 bar_render_done: invoke DrawNumber,240,15,bankroll,00E5D6B0h mov eax, oldX mov playerX, eax mov eax, oldY mov playerY, eax mov eax, oldAngle mov playerAngle, eax mov eax, oldHorizon mov viewHorizon3D, eax mov eax, oldEye mov viewEye3D, eax mov eax, oldMode mov engineMode, eax call UpdateCamera3D ret RenderBar3D ENDP RenderCityGuards3D PROC uses ebx mov eax, cityClock3D xor edx, edx mov ecx, 180 div ecx and eax, 7 mov cityGuardLeft3D.frame, eax cmp arrivalState3D, ARRIVAL_WALK je city_world_guard_right mov cityGuardLeft3D.frame, 15 cmp arrivalState3D, ARRIVAL_STOP jne city_world_guard_invite mov eax, arrivalTime3D xor edx, edx mov ecx, 90 div ecx cmp eax, 7 jbe @F mov eax, 7 @@: add eax, 8 mov cityGuardLeft3D.frame, eax jmp city_world_guard_right city_world_guard_invite: cmp arrivalState3D, ARRIVAL_ADMIT jb city_world_guard_right mov eax, arrivalTime3D xor edx, edx mov ecx, 120 div ecx add eax, 16 cmp eax, 22 jbe @F mov eax, 22 @@: mov cityGuardLeft3D.frame, eax city_world_guard_right: mov eax, cityClock3D add eax, 590 xor edx, edx mov ecx, 220 div ecx and eax, 7 cmp arrivalState3D, ARRIVAL_PROFILE jne @F add eax, 24 @@: mov cityGuardRight3D.frame, eax invoke DrawCityObject3D, OFFSET cityGuardLeft3D,0 invoke DrawCityObject3D, OFFSET cityGuardRight3D,0 ret RenderCityGuards3D ENDP CanWalkHall3D PROC px:DWORD,py:DWORD cmp hallLevel3D, 0 jne hall_collision_bunker ; Stage and wings are solid: the audience stays in the cabaret's front aisle. cmp px, 10*FP_1-9830 jl hall_collision_stage_clear cmp px, 22*FP_1+9830 jg hall_collision_stage_clear cmp py, 2*FP_1-9830 jl hall_collision_stage_clear cmp py, 5*FP_1+9830 jle hall_collision_blocked hall_collision_stage_clear: cmp px, FP_1*47/4 jl hall_collision_cabinets cmp px, FP_1*49/4 jg hall_collision_pool_divider cmp py, 24*FP_1-9830 jl hall_collision_cabinets cmp py, FP_1*51/2+9830 jle hall_collision_blocked cmp py, FP_1*55/2-9830 jl hall_collision_cabinets cmp py, 30*FP_1+9830 jle hall_collision_blocked hall_collision_pool_divider: cmp px, 23*FP_1-9830 jl hall_collision_cabinets cmp px, FP_1*116/5+9830 jg hall_collision_cabinets cmp py, 23*FP_1-9830 jl hall_collision_cabinets cmp py, 24*FP_1+9830 jle hall_collision_blocked cmp py, 26*FP_1-9830 jl hall_collision_cabinets cmp py, 29*FP_1+9830 jle hall_collision_blocked hall_collision_cabinets: ; The work aisle is staff-only; player interaction is from the serving side. cmp px, 3*FP_1-9830 jl hall_collision_counter cmp px, 9*FP_1+9830 jg hall_collision_counter cmp py, 21*FP_1-9830 jl hall_collision_counter cmp py, 24*FP_1+9830 jle hall_collision_blocked hall_collision_counter: cmp px, 3*FP_1-9830 jl hall_collision_dining cmp px, 9*FP_1+9830 jg hall_collision_pool cmp py, 23*FP_1-9830 jl hall_collision_clear cmp py, 24*FP_1+9830 jg hall_collision_dining xor eax, eax ret hall_collision_pool: cmp px, 26*FP_1-9830 jl hall_collision_dining cmp px, 28*FP_1+9830 jg hall_collision_dining cmp py, 24*FP_1-9830 jl hall_collision_clear cmp py, 26*FP_1+9830 jg hall_collision_dining xor eax, eax ret hall_collision_bunker: cmp px, 14*FP_1-9830 jl hall_collision_clear cmp px, 20*FP_1+9830 jg hall_collision_clear cmp py, 14*FP_1-9830 jl hall_collision_clear cmp py, 15*FP_1+9830 jg hall_collision_clear hall_collision_blocked: xor eax, eax ret hall_collision_clear: mov eax, 1 ret hall_collision_dining: cmp py, 26*FP_1+FP_1/3 jl hall_collision_clear cmp py, 29*FP_1+FP_HALF jg hall_collision_clear cmp px, 2*FP_1 jl hall_collision_clear cmp px, 5*FP_1 jle hall_collision_blocked cmp px, 8*FP_1 jl hall_collision_clear cmp px, 11*FP_1 jle hall_collision_blocked jmp hall_collision_clear CanWalkHall3D ENDP RenderCityFacade3D PROC uses ebx esi ; The original entrance artwork carries CASINO itself, mapped once across the doorway. invoke DrawCityBoxSides3D,21*FP_1,24*FP_1,8*FP_1,8*FP_1+FP_1/5,0,FP_1*9/5,57 invoke DrawCityVertical3D,1,8*FP_1+FP_1/5,21*FP_1,24*FP_1,0,FP_1*9/5,58 invoke DrawCityBoxSides3D,FP_1*104/5,FP_1*106/5,8*FP_1,8*FP_1+FP_1*2/5,0,2*FP_1,103 invoke DrawCityBoxSides3D,FP_1*119/5,FP_1*121/5,8*FP_1,8*FP_1+FP_1*2/5,0,2*FP_1,103 invoke DrawCityBoxSides3D,FP_1*104/5,FP_1*121/5,8*FP_1,8*FP_1+FP_HALF,FP_1*9/5,2*FP_1,86 invoke DrawCityHorizontal3D,FP_1*104/5,FP_1*121/5,8*FP_1,8*FP_1+FP_HALF,2*FP_1,85 invoke DrawCityVertical3D,1,8*FP_1+FP_1*41/100,FP_1*101/5,FP_1*104/5,FP_1,FP_1*8/5,105 invoke DrawCityVertical3D,1,8*FP_1+FP_1*41/100,FP_1*121/5,FP_1*124/5,FP_1,FP_1*8/5,105 ; Recessed street windows, projecting bays and ledges are fixed world surfaces. mov esi, 11 hall_city_pilaster: cmp esi, 20 je hall_city_next_pilaster cmp esi, 24 je hall_city_next_pilaster mov ebx, esi shl ebx, 16 lea eax, [ebx+FP_1/5] invoke DrawCityBoxSides3D, ebx,eax,12*FP_1,12*FP_1+FP_1/6,0,5*FP_1,57 lea eax, [ebx+FP_1/5] invoke DrawCityBoxSides3D, ebx,eax,17*FP_1-FP_1/6,17*FP_1,0,5*FP_1,57 lea eax, [ebx+FP_1/5] lea edx, [ebx+FP_1*7/5] invoke DrawCityBoxSides3D,eax,edx,FP_1*237/20,12*FP_1,FP_1/20,FP_1*13/10,57 lea eax, [ebx+FP_1/5] lea edx, [ebx+FP_1*7/5] invoke DrawCityVertical3D,1,12*FP_1+FP_1/25,eax,edx,FP_1/20,FP_1*13/10,62 lea eax, [ebx+FP_1/5] lea edx, [ebx+FP_1*7/5] invoke DrawCityBoxSides3D,eax,edx,12*FP_1,12*FP_1+FP_1/5,2*FP_1,FP_1*18/5,57 lea eax, [ebx+FP_1/5] lea edx, [ebx+FP_1*7/5] invoke DrawCityVertical3D,1,12*FP_1+FP_1/5,eax,edx,2*FP_1,FP_1*18/5,106 lea eax, [ebx+FP_1/5] lea edx, [ebx+FP_1*7/5] invoke DrawCityBoxSides3D,eax,edx,17*FP_1-FP_1/4,17*FP_1,2*FP_1,FP_1*18/5,57 lea eax, [ebx+FP_1/5] lea edx, [ebx+FP_1*7/5] invoke DrawCityVertical3D,1,17*FP_1-FP_1/4,eax,edx,2*FP_1,FP_1*18/5,106 hall_city_next_pilaster: add esi, 4 cmp esi, 29 jb hall_city_pilaster ret RenderCityFacade3D ENDP UpdateHall3D PROC call UpdatePatrons3D mov viewHorizon3D, HALF_H mov viewEye3D, FP_1*17/20 cmp hallLevel3D, 0 je @F mov eax, playerY sub eax, 24*FP_1 jle @F shr eax, 2 cmp eax, FP_1 jbe hall_step_eye mov eax, FP_1 hall_step_eye: add viewEye3D, eax @@: cmp barIntoxication3D, 0 je hall_update_done mov eax, deltaTime3D imul eax, 2 sub barIntoxication3D, eax jnc @F mov barIntoxication3D, 0 @@: mov eax, animationClock3D shr eax, 3 and eax, 511 mov eax, tableSin[eax*4] imul barIntoxication3D shrd eax, edx, 16 sar eax, 14 add viewHorizon3D, eax hall_update_done: ret UpdateHall3D ENDP ; Absolute route phase avoids accumulated movement drift and keeps walking FPS-independent. UpdatePatrons3D PROC uses ebx esi edi local wx:DWORD,wy:DWORD,phase:DWORD cmp hallLevel3D, 0 jne patron_update_done mov eax, deltaTime3D add eax, patronClock3D xor edx, edx mov ecx, 20000 div ecx mov patronClock3D, edx xor esi, esi patron_update_loop: mov eax, esi imul eax, 10000 add eax, patronClock3D xor edx, edx mov ecx, 20000 div ecx mov eax, edx xor edx, edx mov ecx, 5000 div ecx mov phase, eax mov eax, edx imul eax, 4*FP_1 xor edx, edx div ecx mov wx, 18*FP_1 mov wy, 18*FP_1 cmp phase, 0 jne patron_route_south add wx, eax jmp patron_route_check patron_route_south: cmp phase, 1 jne patron_route_west mov wx, 22*FP_1 shr eax, 1 add wy, eax jmp patron_route_check patron_route_west: mov wy, 20*FP_1 cmp phase, 2 jne patron_route_north mov wx, 22*FP_1 sub wx, eax jmp patron_route_check patron_route_north: shr eax, 1 sub wy, eax patron_route_check: invoke CanWalk3D,wx,wy test eax, eax jz patron_update_next imul ebx, esi, SIZEOF CityProp3D mov eax, wx mov hallGuests3D[ebx].posX, eax mov eax, wy mov hallGuests3D[ebx].posY, eax mov eax, patronClock3D xor edx, edx mov ecx, 220 div ecx and eax, 3 lea eax, [eax+esi*4] mov hallGuests3D[ebx].frame, eax patron_update_next: inc esi cmp esi, 2 jb patron_update_loop patron_update_done: ret UpdatePatrons3D ENDP RenderPatrons3D PROC uses esi xor esi, esi patron_render_loop: imul eax, esi, SIZEOF CityProp3D add eax, OFFSET hallGuests3D invoke DrawCityObject3D,eax,0 inc esi cmp esi, HALL_PATRON_COUNT jb patron_render_loop ret RenderPatrons3D ENDP