diff --git a/build/graphics/poker.exe b/build/graphics/poker.exe index 8d15494..f017cf7 100644 Binary files a/build/graphics/poker.exe and b/build/graphics/poker.exe differ diff --git a/poker.exe b/poker.exe index 74e1c3c..f017cf7 100644 Binary files a/poker.exe and b/poker.exe differ diff --git a/poker.obj b/poker.obj index c8af985..ac52956 100644 Binary files a/poker.obj and b/poker.obj differ diff --git a/runtime/casino3d-progress.dat b/runtime/casino3d-progress.dat index 93da86e..8be1bee 100644 Binary files a/runtime/casino3d-progress.dat and b/runtime/casino3d-progress.dat differ diff --git a/src_graphics/hall_data.inc b/src_graphics/hall_data.inc index 58637d9..6021a9d 100644 --- a/src_graphics/hall_data.inc +++ b/src_graphics/hall_data.inc @@ -25,6 +25,9 @@ RenderPatrons3D PROTO ResolveWorldSurface3D PROTO :DWORD include src_graphics\hall_layout.inc .data +ALIGN 4 +tableInvDelta3D dd 241 dup(0) +tableStepRatio3D dd 241 dup(0) hallSign3D AnimPack3D <> hallSignPath3D db "assets\animations\hall\sign.bin",0 hallCityDetail3D AnimPack3D <> diff --git a/src_graphics/hall_scene.inc b/src_graphics/hall_scene.inc index ffcf563..85f4a5e 100644 --- a/src_graphics/hall_scene.inc +++ b/src_graphics/hall_scene.inc @@ -1,1306 +1,1329 @@ -; 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 - mov eax, hallStationPacks3D[esi*4] - test eax, eax - jz hall_sprite_draw - ; Missing dedicated art hides the station, never shows a contaminated atlas cell. - cmp [eax].AnimPack3D.pixels, 0 - je hall_sprite_next - mov hallSpriteTemp3D.asset, eax - cmp eax, OFFSET hallSlotFurniture3D - jne hall_sprite_draw - mov eax, animationClock3D - xor edx, edx - mov ecx, 320 - div ecx - add eax, esi - xor edx, edx - mov ecx, 3 - div ecx - mov hallSpriteTemp3D.frame, edx -hall_sprite_draw: - 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 - call InitCasinoNpcMind - call InitNpcLife3D - ret -InitHall3D ENDP - -LoadHallAssets3D PROC - invoke LoadAnimPack3D, OFFSET hallCardFurniture3D, OFFSET hallCardFurniturePath3D - invoke LoadAnimPack3D, OFFSET hallWheelFurniture3D, OFFSET hallWheelFurniturePath3D - invoke LoadAnimPack3D, OFFSET hallBackFurniture3D, OFFSET hallBackFurniturePath3D - invoke LoadAnimPack3D, OFFSET hallSkillFurniture3D, OFFSET hallSkillFurniturePath3D - invoke LoadAnimPack3D, OFFSET hallChairFurniture3D, OFFSET hallChairFurniturePath3D - invoke LoadAnimPack3D, OFFSET hallLampFurniture3D, OFFSET hallLampFurniturePath3D - invoke LoadAnimPack3D, OFFSET hallRopeFurniture3D, OFFSET hallRopeFurniturePath3D - invoke LoadAnimPack3D, OFFSET hallSlotFurniture3D, OFFSET hallSlotFurniturePath3D - 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 - call FreeNativeWheelArt3D - call FreeNativeCoinArt3D - invoke FreeAnimPack3D, OFFSET hallCardFurniture3D - invoke FreeAnimPack3D, OFFSET hallWheelFurniture3D - invoke FreeAnimPack3D, OFFSET hallBackFurniture3D - invoke FreeAnimPack3D, OFFSET hallSkillFurniture3D - invoke FreeAnimPack3D, OFFSET hallChairFurniture3D - invoke FreeAnimPack3D, OFFSET hallLampFurniture3D - invoke FreeAnimPack3D, OFFSET hallRopeFurniture3D - invoke FreeAnimPack3D, OFFSET hallSlotFurniture3D - 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 - xor esi, esi -hall_render_lounge: - imul eax, esi, SIZEOF CityProp3D - add eax, OFFSET hallLoungeDecor3D - invoke DrawCityObject3D,eax,0 - inc esi - cmp esi, HALL_DECOR_COUNT - jb hall_render_lounge - 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 - call CheckNpcInteractions3D - 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_NPC - jne @F - call OpenNpcDialog3D - ret -@@: - - 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 uses esi ebx px:DWORD,py:DWORD - cmp hallLevel3D, 0 - jne hall_collision_bunker - xor esi, esi -hall_collision_decor: - imul ebx, esi, SIZEOF CityProp3D - mov eax, px - sub eax, hallLoungeDecor3D[ebx].posX - test eax, eax - jns @F - neg eax -@@: - mov ecx, hallLoungeDecor3D[ebx].worldW - shr ecx, 1 - add ecx, 9830 - cmp eax, ecx - ja hall_collision_next_decor - mov eax, py - sub eax, hallLoungeDecor3D[ebx].posY - test eax, eax - jns @F - neg eax -@@: - cmp eax, FP_1*2/5 - jbe hall_collision_blocked -hall_collision_next_decor: - inc esi - cmp esi, HALL_DECOR_COUNT - jb hall_collision_decor - ; 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 - call UpdateNpcLife3D - ret -IF 0 - 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: - call UpdateCasinoNpcMind - ret -ENDIF -UpdatePatrons3D ENDP - -RenderPatrons3D PROC uses esi - call RenderNpcLife3D - mov esi,8 -patron_render_loop: - imul eax, esi, SIZEOF CityProp3D - add eax, OFFSET hallGuests3D - invoke DrawCityObject3D,eax,0 - inc esi - cmp esi,12 - jb patron_render_loop - ret -RenderPatrons3D ENDP +; 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: + cmp ecx, 240 + jbe @F + mov ecx, 240 +@@: + cmp ecx, 1 + jae @F + mov ecx, 1 +@@: + push eax + mov edx, [tableInvDelta3D + ecx*4] + imul edx + shrd eax, edx, 16 + 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 +@@: + pop eax + mov edx, [tableStepRatio3D + ecx*4] + imul edx + shrd eax, edx, 16 + mov ebx, eax + mov eax, playerPlaneX + imul ebx + shrd eax, edx, 16 + mov sx, eax + mov eax, playerPlaneY + imul ebx + shrd eax, edx, 16 + 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 + mov eax, hallStationPacks3D[esi*4] + test eax, eax + jz hall_sprite_draw + ; Missing dedicated art hides the station, never shows a contaminated atlas cell. + cmp [eax].AnimPack3D.pixels, 0 + je hall_sprite_next + mov hallSpriteTemp3D.asset, eax + cmp eax, OFFSET hallSlotFurniture3D + jne hall_sprite_draw + mov eax, animationClock3D + xor edx, edx + mov ecx, 320 + div ecx + add eax, esi + xor edx, edx + mov ecx, 3 + div ecx + mov hallSpriteTemp3D.frame, edx +hall_sprite_draw: + 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 + ; Precalculate floor & ceiling distance / step LUTs (no division at runtime) + mov ecx, 1 +init_plane_lut: + cmp ecx, 241 + jge init_plane_done + mov eax, SCREEN_H * FP_1 + xor edx, edx + div ecx + mov [tableInvDelta3D + ecx*4], eax + mov eax, 98304 + xor edx, edx + div ecx + mov [tableStepRatio3D + ecx*4], eax + inc ecx + jmp init_plane_lut +init_plane_done: + call InitCasinoNpcMind + call InitNpcLife3D + ret +InitHall3D ENDP + +LoadHallAssets3D PROC + invoke LoadAnimPack3D, OFFSET hallCardFurniture3D, OFFSET hallCardFurniturePath3D + invoke LoadAnimPack3D, OFFSET hallWheelFurniture3D, OFFSET hallWheelFurniturePath3D + invoke LoadAnimPack3D, OFFSET hallBackFurniture3D, OFFSET hallBackFurniturePath3D + invoke LoadAnimPack3D, OFFSET hallSkillFurniture3D, OFFSET hallSkillFurniturePath3D + invoke LoadAnimPack3D, OFFSET hallChairFurniture3D, OFFSET hallChairFurniturePath3D + invoke LoadAnimPack3D, OFFSET hallLampFurniture3D, OFFSET hallLampFurniturePath3D + invoke LoadAnimPack3D, OFFSET hallRopeFurniture3D, OFFSET hallRopeFurniturePath3D + invoke LoadAnimPack3D, OFFSET hallSlotFurniture3D, OFFSET hallSlotFurniturePath3D + 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 + call FreeNativeWheelArt3D + call FreeNativeCoinArt3D + invoke FreeAnimPack3D, OFFSET hallCardFurniture3D + invoke FreeAnimPack3D, OFFSET hallWheelFurniture3D + invoke FreeAnimPack3D, OFFSET hallBackFurniture3D + invoke FreeAnimPack3D, OFFSET hallSkillFurniture3D + invoke FreeAnimPack3D, OFFSET hallChairFurniture3D + invoke FreeAnimPack3D, OFFSET hallLampFurniture3D + invoke FreeAnimPack3D, OFFSET hallRopeFurniture3D + invoke FreeAnimPack3D, OFFSET hallSlotFurniture3D + 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 + xor esi, esi +hall_render_lounge: + imul eax, esi, SIZEOF CityProp3D + add eax, OFFSET hallLoungeDecor3D + invoke DrawCityObject3D,eax,0 + inc esi + cmp esi, HALL_DECOR_COUNT + jb hall_render_lounge + 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 + call CheckNpcInteractions3D + 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_NPC + jne @F + call OpenNpcDialog3D + ret +@@: + + 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 uses esi ebx px:DWORD,py:DWORD + cmp hallLevel3D, 0 + jne hall_collision_bunker + xor esi, esi +hall_collision_decor: + imul ebx, esi, SIZEOF CityProp3D + mov eax, px + sub eax, hallLoungeDecor3D[ebx].posX + test eax, eax + jns @F + neg eax +@@: + mov ecx, hallLoungeDecor3D[ebx].worldW + shr ecx, 1 + add ecx, 9830 + cmp eax, ecx + ja hall_collision_next_decor + mov eax, py + sub eax, hallLoungeDecor3D[ebx].posY + test eax, eax + jns @F + neg eax +@@: + cmp eax, FP_1*2/5 + jbe hall_collision_blocked +hall_collision_next_decor: + inc esi + cmp esi, HALL_DECOR_COUNT + jb hall_collision_decor + ; 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 + call UpdateNpcLife3D + ret +IF 0 + 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: + call UpdateCasinoNpcMind + ret +ENDIF +UpdatePatrons3D ENDP + +RenderPatrons3D PROC uses esi + call RenderNpcLife3D + mov esi,8 +patron_render_loop: + imul eax, esi, SIZEOF CityProp3D + add eax, OFFSET hallGuests3D + invoke DrawCityObject3D,eax,0 + inc esi + cmp esi,12 + jb patron_render_loop + ret +RenderPatrons3D ENDP