; 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,2 jne @F mov material,125 @@: cmp hallLevel3D, 1 jne @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 ebx,122 jb hall_plane_existing_hd cmp ebx,125 ja hall_plane_existing_hd mov ecx,upperMaterialArt3D.pixels test ecx,ecx jz hall_plane_upper_fallback sub ebx,122 shl ebx,18 add ecx,ebx jmp hall_plane_hd_coordinates hall_plane_upper_fallback: mov ebx,85 mov material,ebx hall_plane_existing_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 hall_plane_hd_coordinates: 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 engineMode,MODE_STAIRS je hall_sprites_done cmp hallLevel3D, 0 jne hall_sprites_done xor esi, esi hall_sprite_loop: cmp esi,3 jb hall_sprite_next cmp esi,17 je hall_sprite_next cmp esi,19 je hall_sprite_next cmp esi,21 je hall_sprite_next cmp esi, 14 je hall_sprite_next cmp esi, 15 je hall_sprite_next cmp esi, 16 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, HALL_STANDING_EYE 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 PositionTerminalStations3D mov scrStation3D.posX,29*FP_1 mov scrStation3D.posY,FP_1*15/2 call InitNpcLife3D call BuildHallLighting3D ret InitHall3D ENDP LoadHallAssets3D PROC call InitUpperSurfaces3D invoke LoadAnimPack3D, OFFSET upperMaterialArt3D, OFFSET upperMaterialPath3D invoke ValidateCityPack3D, OFFSET upperMaterialArt3D,256,256,4 invoke LoadAnimPack3D,OFFSET upperGamesArt3D,OFFSET upperGamesPath3D invoke ValidateCityPack3D,OFFSET upperGamesArt3D,320,224,2 invoke LoadAnimPack3D,OFFSET upperPortalArt3D,OFFSET upperPortalPath3D invoke ValidateCityPack3D,OFFSET upperPortalArt3D,256,512,13 invoke LoadAnimPack3D,OFFSET upperLeverArt3D,OFFSET upperLeverPath3D invoke ValidateCityPack3D,OFFSET upperLeverArt3D,128,224,6 invoke LoadAnimPack3D,OFFSET upperBarArt3D,OFFSET upperBarPath3D invoke ValidateCityPack3D,OFFSET upperBarArt3D,160,256,2 invoke LoadAnimPack3D,OFFSET upperBrazierArt3D,OFFSET upperBrazierPath3D invoke ValidateCityPack3D,OFFSET upperBrazierArt3D,128,256,4 invoke LoadAnimPack3D, OFFSET cabaretArt3D, OFFSET cabaretPath3D invoke ValidateCityPack3D, OFFSET cabaretArt3D,160,256,12 invoke LoadAnimPack3D, OFFSET cabaretPianoArt3D, OFFSET cabaretPianoPath3D invoke ValidateCityPack3D, OFFSET cabaretPianoArt3D,384,256,4 invoke LoadAnimPack3D, OFFSET cabaretGuitarArt3D, OFFSET cabaretGuitarPath3D invoke ValidateCityPack3D, OFFSET cabaretGuitarArt3D,160,256,4 invoke LoadAnimPack3D, OFFSET npcGuestArt3D, OFFSET npcGuestPath3D invoke ValidateCityPack3D, OFFSET npcGuestArt3D,128,224,36 call LoadWorldDressing3D call LoadScratchArt3D 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 invoke FreeAnimPack3D, OFFSET upperMaterialArt3D invoke FreeAnimPack3D,OFFSET upperGamesArt3D invoke FreeAnimPack3D,OFFSET upperPortalArt3D invoke FreeAnimPack3D,OFFSET upperLeverArt3D invoke FreeAnimPack3D,OFFSET upperBarArt3D invoke FreeAnimPack3D,OFFSET upperBrazierArt3D invoke FreeAnimPack3D, OFFSET cabaretArt3D invoke FreeAnimPack3D, OFFSET cabaretPianoArt3D invoke FreeAnimPack3D, OFFSET cabaretGuitarArt3D invoke FreeAnimPack3D, OFFSET npcGuestArt3D call FreeWorldDressing3D 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 engineMode,MODE_STAIRS jne @F call RenderWalkableStairs3D call RenderInteriorViews3D ret @@: cmp hallLevel3D,2 jne @F call RenderUpperLounge3D call RenderInteriorViews3D ret @@: 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 cmp esi,6 je hall_render_next_prop 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 call RenderCabaretActors3D invoke DrawCityObject3D,OFFSET scrStation3D,0 call RenderHallDressing3D call RenderHallPortals3D call ApplyHallLighting3D call RenderInteriorViews3D ret hall_render_bunker: 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 call RenderInteriorViews3D 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,2 jne @F call CheckUpperInteractions3D ret @@: 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,16*FP_1,FP_1*73/10,INTERACT_CABARET_SEAT,OFFSET cabaretSeat3D invoke HallTarget3D,scrStation3D.posX,scrStation3D.posY,INTERACT_SCRATCH,OFFSET scratchHall3D invoke HallTarget3D,FP_1*285/10,FP_1*105/10,INTERACT_BASEMENT,OFFSET hallBunkerPrompt3D invoke HallTarget3D, 27*FP_1,25*FP_1,INTERACT_BILLIARDS,OFFSET hallPoolPrompt3D invoke HallTarget3D,FP_1*305/10,21*FP_1,0,OFFSET upperStairPrompt3D 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 ret CheckHallInteractions3D ENDP TriggerHallInteraction3D PROC call TryUpperInteraction3D test eax,eax jnz hall_trigger_handled cmp interactTargetId,INTERACT_CABARET_SEAT jne @F call BeginCabaret3D mov eax,1 ret @@: cmp interactTargetId,INTERACT_SCRATCH jne @F call BeginScratch3D mov eax,1 ret @@: 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 jne hall_trigger_none ; The button opens the steel leaf. Walking across its threshold is separate. mov basementDoorTarget3D,FP_1 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 hall_trigger_handled: ret TriggerHallInteraction3D ENDP HandleStairs3D PROC call UpdateStairHeight3D call HandlePlayerInput call UpdateInteriorTravel3D call UpdateStairHeight3D ret HandleStairs3D ENDP RenderStairs3D PROC uses ebx esi call UpdateCamera3D call RenderCeilingAndFloor call RenderWalls call PrepareCityDepth3D call RenderHall3D call RenderHUD 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 cmp hallLevel3D,2 jne @F mov playerX,FP_1*25/2 mov playerY,16*FP_1+FP_1/3 @@: mov viewHorizon3D, HALF_H mov viewEye3D, HALL_STANDING_EYE 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 mov eax,OFFSET barName3D cmp hallLevel3D,2 jne @F mov eax,OFFSET upperBarName3D @@: invoke DrawString8x8, 18,15,eax,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 engineMode,MODE_STAIRS jne @F mov eax,1 ret @@: cmp hallLevel3D,2 jne @F invoke UpperCanWalk3D,px,py ret @@: cmp hallLevel3D, 0 jne hall_collision_bunker invoke ScratchCanWalk3D,px,py test eax,eax jz hall_collision_blocked invoke InteriorDoorCanWalk3D,px,py test eax,eax jz hall_collision_blocked invoke TerminalCanWalk3D,px,py test eax,eax jz hall_collision_blocked 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 UpdateCabaret3D call UpdateInteriorTravel3D call UpdatePatrons3D call UpdateInteriorEye3D 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 include src_graphics\world_dressing.inc include src_graphics\terminal.inc include src_graphics\hall_expansion.inc include src_graphics\interior_portals.inc include src_graphics\cabaret.inc include src_graphics\hall_lighting.inc