; Native roulette: one cylinder, one paid stake, cinematic states in the same window. .code FreeRussianAssets3D PROC invoke FreeAnimPack3D,OFFSET rrPartyArt3D invoke FreeAnimPack3D,OFFSET rr3d_room invoke FreeAnimPack3D,OFFSET rr3d_dealer invoke FreeAnimPack3D,OFFSET rr3d_hands cmp rr3d_assets, 0 je @F invoke HeapFree, rr3d_heap, 0, rr3d_assets mov rr3d_assets, 0 @@: ret FreeRussianAssets3D ENDP LoadRussianAssets3D PROC uses ebx local count:DWORD cmp rr3d_assets, 0 jne rr_assets_ready invoke CreateFileA, OFFSET rrAssetPath3D, 80000000h, 1, 0, 3, 80h, 0 cmp eax, -1 je rr_assets_failed mov ebx, eax invoke GetProcessHeap mov rr3d_heap, eax invoke HeapAlloc, eax, 0, 4194304 mov rr3d_assets, eax test eax, eax jz rr_assets_close_failed invoke ReadFile, ebx, rr3d_assets, 4194304, ADDR count, 0 test eax, eax jz rr_assets_close_failed cmp count, 4194304 jne rr_assets_close_failed invoke CloseHandle, ebx rr_assets_ready: invoke LoadAnimPack3D,OFFSET rr3d_room,OFFSET bs3d_roomPath test eax,eax jz rr_view_failed cmp rr3d_room.imageWidth,640 jne rr_view_failed cmp rr3d_room.height,480 jne rr_view_failed invoke LoadAnimPack3D,OFFSET rr3d_dealer,OFFSET bs3d_dealerPath test eax,eax jz rr_view_failed cmp rr3d_dealer.frames,16 jne rr_view_failed invoke LoadAnimPack3D,OFFSET rr3d_hands,OFFSET rrHandsPath3D test eax,eax jz rr_view_failed cmp rr3d_hands.frames,16 jne rr_view_failed mov eax, 1 ret rr_view_failed: call FreeRussianAssets3D jmp rr_assets_failed rr_assets_close_failed: invoke CloseHandle, ebx call FreeRussianAssets3D rr_assets_failed: invoke MessageBoxA, h3DWnd, OFFSET rrAssetError3D, OFFSET rrTitleText3D, 10h xor eax, eax ret LoadRussianAssets3D ENDP InitRussianGame3D PROC call LoadRussianAssets3D test eax, eax jz @F mov rr3d_state, RR_READY mov rr3d_timer, 0 mov rr3d_payout, 0 mov rr3d_error, 0 mov cardExitActive3D,0 call RRPartyInit3D @@: ret InitRussianGame3D ENDP StartRussianRound3D PROC uses ebx esi cmp rr3d_state, RR_READY jne rr_start_done mov eax, rr3d_bet test eax, eax jle rr_start_empty cmp eax, bankroll ja rr_start_empty cmp rr3d_bullets, 1 jb rr_start_empty cmp rr3d_bullets, 3 ja rr_start_empty call RRPartyReset3D mov eax,rr3d_bet mov rrPartyStake3D,eax mov rrPartySettled3D,0 sub bankroll, eax mov rr3d_payout, 0 mov rr3d_error, 0 call RRLoadCylinder3D invoke RandomRange3D, rrPartySize3D mov rr3d_turn, eax mov rr3d_state, RR_LOAD mov rr3d_timer, 0 invoke PlayCasinoSound3D, OFFSET rrLoadSound3D, 0, 20003h call SaveProgress3D jmp rr_start_done rr_start_empty: mov rr3d_error, 1 rr_start_done: ret StartRussianRound3D ENDP AdvanceRussianScene3D PROC mov eax, rr3d_state cmp eax, RR_LOAD je rr_next_spin cmp eax, RR_SPIN je rr_next_turn cmp eax, RR_RAISE je rr_next_fire cmp eax, RR_FIRE je rr_next_outcome cmp eax, RR_DRY je rr_next_chamber cmp eax, RR_GLITCH je rr_next_dark cmp eax, RR_DARK je rr_next_gman cmp eax, RR_GMAN je rr_next_return cmp eax, RR_RETURN je rr_next_casino ret rr_next_spin: mov rr3d_state, RR_SPIN ret rr_next_turn: mov rr3d_state, RR_PLAYER cmp rr3d_turn, 0 je @F mov rr3d_state, RR_RAISE @@: ret rr_next_fire: mov eax, rr3d_chamber bt rr3d_mask, eax setc al movzx eax, al mov rr3d_shot, eax cmp rr3d_turn, 0 jne @F cmp rr3d_shot, 0 je @F invoke TriggerCharmBulletProtection test eax, eax jz @F mov rr3d_shot, 0 @@: mov rr3d_state, RR_FIRE cmp rr3d_shot, 0 jz @F invoke PlayCasinoSound3D, OFFSET rrFireSound3D, 0, 20003h ret @@: invoke PlayCasinoSound3D, OFFSET rrDrySound3D, 0, 20003h ret rr_next_outcome: cmp rr3d_shot, 0 jne rr_live_round mov rr3d_state, RR_DRY ret rr_live_round: cmp rrPartyCount3D,1 jbe @F call RRPartyHit3D ret @@: cmp rr3d_turn, 0 je rr_player_lost mov eax, rr3d_bet shl eax, 1 mov rr3d_payout, eax add bankroll, eax mov rr3d_state, RR_WIN mov rrPartySettled3D,1 invoke TriggerCharmWinBonus,rr3d_bet invoke PlayCasinoSound3D, OFFSET soundRoundWin, 0, 20003h invoke NpcRecordRound3D,6,1 call SaveProgress3D ret rr_player_lost: mov rrPartySettled3D,1 invoke TriggerCharmLossProtection,rr3d_bet mov rr3d_state, RR_GLITCH invoke NpcRecordRound3D,6,0 call SaveProgress3D ret rr_next_chamber: cmp rrPartyCount3D,1 jbe @F call RRPartyNext3D ret @@: inc rr3d_chamber cmp rr3d_chamber, 6 jb @F mov rr3d_chamber, 0 @@: xor rr3d_turn, 1 jmp rr_next_turn rr_next_dark: mov rr3d_state, RR_DARK ret rr_next_gman: mov rr3d_state, RR_GMAN invoke GetFileAttributesA, OFFSET rrGmanSound3D cmp eax, -1 je @F invoke PlayCasinoSound3D, OFFSET rrGmanSound3D, 0, 20003h @@: ret rr_next_return: mov rr3d_state, RR_RETURN invoke PlayCasinoSound3D, 0, 0, 0 ret rr_next_casino: mov rr3d_state, RR_READY mov rr3d_timer, 0 mov engineMode, MODE_WALK mov paused3D, 0 ret AdvanceRussianScene3D ENDP HandleRussianInput3D PROC uses ebx call HandleCardExit3D test eax,eax jnz rr_input_done call RRPartyInput3D cmp cardExitActive3D,0 jne rr_input_done cmp engineMode,MODE_RUSSIAN jne rr_input_done cmp rr3d_state, RR_READY je rr_input_ready cmp rr3d_state, RR_PLAYER je rr_input_fire cmp rr3d_state, RR_WIN je rr_input_next mov eax, deltaTime3D add rr3d_timer, eax rr_update_scene: mov ebx, rr3d_state mov ecx, rr3d_durations[ebx*4] test ecx, ecx jz rr_input_done cmp rr3d_timer, ecx jb rr_input_done sub rr3d_timer, ecx call AdvanceRussianScene3D cmp engineMode, MODE_WALK jne rr_update_scene ret rr_input_ready: invoke Pressed3D, 57h test eax, eax jnz rr_bet_up invoke Pressed3D, 26h test eax, eax jnz rr_bet_up invoke Pressed3D, 53h test eax, eax jnz rr_bet_down invoke Pressed3D, 28h test eax, eax jnz rr_bet_down invoke Pressed3D, 31h test eax, eax jz @F mov rr3d_bullets, 1 @@: invoke Pressed3D, 32h test eax, eax jz @F mov rr3d_bullets, 2 @@: invoke Pressed3D, 33h test eax, eax jz @F mov rr3d_bullets, 3 @@: invoke Pressed3D, 20h test eax, eax jz rr_input_done call StartRussianRound3D ret rr_bet_up: mov eax, rr3d_bet add eax, 50 cmp eax, 5000 jbe @F mov eax, 5000 @@: cmp eax, bankroll jbe @F mov eax, bankroll @@: mov rr3d_bet, eax mov rr3d_error, 0 ret rr_bet_down: sub rr3d_bet, 50 cmp rr3d_bet, 50 jge rr_input_done mov rr3d_bet, 50 ret rr_input_fire: invoke Pressed3D, 20h test eax, eax jz rr_input_done mov rr3d_state, RR_RAISE mov rr3d_timer, 0 ret rr_input_next: cmp tournamentMode, 1 jne @F invoke Pressed3D, 20h test eax, eax jnz rr_tourney_report invoke Pressed3D, 0Dh test eax, eax jnz rr_tourney_report ret rr_tourney_report: cmp rr3d_state, RR_WIN jne rr_tourney_loss invoke TowerReportRoundOutcome, 1 ret rr_tourney_loss: invoke TowerReportRoundOutcome, 2 ret @@: invoke Pressed3D, 20h test eax, eax jz rr_input_done mov rr3d_state, RR_READY mov rr3d_timer, 0 call RRPartyReset3D rr_input_done: ret HandleRussianInput3D ENDP ; Inverse-mapped rotation keeps every frame aligned and transparent. DrawRouletteSprite3D PROC uses ebx esi edi frame:DWORD,centerX:DWORD,centerY:DWORD,wide:DWORD,tall:DWORD,angle:DWORD local sx:DWORD,sy:DWORD,cosa:DWORD,sina:DWORD,base:DWORD local px:DWORD,py:DWORD,loX:DWORD,hiX:DWORD,hiY:DWORD,rad:DWORD cmp rr3d_assets, 0 je rr_sprite_done mov eax, frame shl eax, 18 add eax, rr3d_assets mov base, eax mov eax, angle and eax, 511 mov ecx, tableSin[eax*4] mov sina, ecx mov ecx, tableCos[eax*4] mov cosa, ecx mov eax, wide add eax, tall shr eax, 1 mov rad, eax mov ecx, centerX sub ecx, eax cmp ecx, 0 jge @F xor ecx, ecx @@: mov loX, ecx add eax, centerX cmp eax, 319 jle @F mov eax, 319 @@: mov hiX, eax mov eax, centerY sub eax, rad cmp eax, 0 jge @F xor eax, eax @@: mov py, eax mov eax, centerY add eax, rad cmp eax, 239 jle @F mov eax, 239 @@: mov hiY, eax ; Axis-aligned frames need only their rectangular footprint. cmp angle, 0 jne rr_sprite_row mov eax, wide shr eax, 1 mov ecx, centerX sub ecx, eax cmp ecx, 0 jge @F xor ecx, ecx @@: mov loX, ecx add eax, centerX cmp eax, 319 jle @F mov eax, 319 @@: mov hiX, eax mov eax, tall shr eax, 1 mov ecx, centerY sub ecx, eax cmp ecx, 0 jge @F xor ecx, ecx @@: mov py, ecx add eax, centerY cmp eax, 239 jle @F mov eax, 239 @@: mov hiY, eax rr_sprite_row: mov eax, loX mov px, eax rr_sprite_pixel: mov eax, px sub eax, centerX mov sx, eax mov ebx, py sub ebx, centerY mov sy, ebx imul eax, cosa imul ebx, sina add eax, ebx sar eax, 8 cdq idiv wide add eax, 128 cmp eax, 255 ja rr_sprite_skip mov esi, eax mov eax, sy imul eax, cosa mov ebx, sx imul ebx, sina sub eax, ebx sar eax, 8 cdq idiv tall add eax, 128 cmp eax, 255 ja rr_sprite_skip shl eax, 8 add eax, esi mov esi, base mov ebx, [esi+eax*4] test ebx, 80000000h jz rr_sprite_skip mov eax, py imul eax, SCREEN_W*2 mov edx, px shl edx, 1 add eax, edx lea edi, screenBuffer[eax*4] mov [edi], ebx mov [edi+4], ebx mov [edi+SCREEN_W*4], ebx mov [edi+SCREEN_W*4+4], ebx rr_sprite_skip: inc px mov eax, px cmp eax, hiX jle rr_sprite_pixel inc py mov eax, py cmp eax, hiY jle rr_sprite_row rr_sprite_done: ret DrawRouletteSprite3D ENDP ; Visual noise never consumes the gameplay RNG or changes the outcome. RenderRussianGlitch3D PROC uses ebx esi edi strength:DWORD local visualSeed:DWORD,scanRow:DWORD mov eax, rr3d_timer xor eax, 13579BDFh mov visualSeed, eax mov scanRow, 0 rr_glitch_row: mov eax, visualSeed imul eax, 1664525 add eax, 1013904223 mov visualSeed, eax shr eax, 16 and eax, 255 cmp eax, strength jae rr_glitch_next mov eax, scanRow imul eax, SCREEN_W*4*2 lea edi, screenBuffer[eax] mov esi, SCREEN_W*2 mov ebx, 001ECFD8h test scanRow, 2 jz @F mov ebx, 00983478h @@: rr_glitch_pixels: xor [edi], ebx add edi, 4 dec esi jnz rr_glitch_pixels rr_glitch_next: add scanRow, 3 cmp scanRow, 240 jb rr_glitch_row ret RenderRussianGlitch3D ENDP RenderRussianCylinder3D PROC uses ebx esi local loaded:DWORD,hx:DWORD,hy:DWORD invoke DrawRouletteSprite3D, 2, 251, 112, 62, 62, 0 mov eax, rr3d_timer xor edx, edx mov ecx, 450 div ecx cmp eax, rr3d_bullets jbe @F mov eax, rr3d_bullets @@: mov loaded, eax xor esi, esi xor ebx, ebx rr_draw_chambers: bt rr3d_mask, esi jnc rr_next_hole cmp ebx, loaded jae rr_next_hole inc ebx imul eax, esi, 512 xor edx, edx mov ecx, 6 div ecx sub eax, 128 and eax, 511 mov edx, tableCos[eax*4] imul edx, 17 sar edx, 16 add edx, 251 mov hx, edx mov edx, tableSin[eax*4] imul edx, 17 sar edx, 16 add edx, 112 mov hy, edx invoke DrawCircle, hx, hy, 7, 00C49B41h invoke DrawCircle, hx, hy, 3, 006E5129h rr_next_hole: inc esi cmp esi, 6 jb rr_draw_chambers ret RenderRussianCylinder3D ENDP RenderRussianHall3D PROC call UpdateCamera3D call RenderCeilingAndFloor call RenderWalls call RenderSprites call RenderHUD call RenderMinimap ret RenderRussianHall3D ENDP RenderRussianGame3D PROC uses ebx esi edi local angle:DWORD,poseY:DWORD,poseX:DWORD,scale:DWORD cmp engineMode, MODE_WALK je rr_render_hall cmp rr3d_state, RR_RETURN je rr_render_return cmp rr3d_state, RR_DARK je rr_render_dark cmp rr3d_state, RR_GMAN je rr_render_gman call RenderRussianTable3D call RenderRussianExit3D ret invoke DrawBox, 0, 0, 320, 240, 00100D17h invoke DrawBoxBorder, 7, 7, 306, 226, 00192320h, 00BE994Ah, 2 invoke DrawBox, 15, 64, 290, 110, 000E3025h invoke DrawBox, 15, 165, 290, 9, 004E2D24h invoke DrawString8x8, 96, 18, OFFSET rrTitleText3D, 00EBD5A0h invoke DrawString8x8, 18, 43, OFFSET textBankPrompt, 003CFF3Ch invoke FormatNumber, bankroll, OFFSET hudNumBuffer invoke DrawString8x8, 66, 43, OFFSET hudNumBuffer, 003CFF3Ch invoke DrawString8x8, 195, 43, OFFSET textBetPrompt, 00FFD700h invoke FormatNumber, rr3d_bet, OFFSET hudNumBuffer invoke DrawString8x8, 251, 43, OFFSET hudNumBuffer, 00FFD700h mov eax, rr3d_state cmp eax, RR_LOAD je rr_render_loading cmp eax, RR_SPIN je rr_render_spin cmp eax, RR_RAISE je rr_render_raise cmp eax, RR_FIRE je rr_render_fire cmp eax, RR_DRY je rr_render_dry cmp eax, RR_WIN je rr_render_win cmp eax, RR_GLITCH je rr_render_glitch invoke DrawRouletteSprite3D, 0, 160, 114, 165, 104, 0 cmp rr3d_state, RR_PLAYER je rr_render_player invoke DrawString8x8, 18, 180, OFFSET rrCartridgeText3D, 00EBD5A0h invoke FormatNumber, rr3d_bullets, OFFSET hudNumBuffer invoke DrawString8x8, 90, 180, OFFSET hudNumBuffer, 00FFFFFFh invoke DrawString8x8, 18, 197, OFFSET rrBulletKeys3D, 00C1C9CFh invoke DrawString8x8, 197, 197, OFFSET rrBetKeys3D, 00C1C9CFh invoke DrawString8x8, 18, 218, OFFSET rrStartText3D, 003CFF3Ch cmp rr3d_error, 0 je rr_render_done invoke DrawString8x8, 18, 181, OFFSET textNoCash3D, 00FF6464h ret rr_render_player: invoke DrawString8x8, 130, 180, OFFSET rrPlayerText3D, 003CFF3Ch invoke DrawString8x8, 55, 207, OFFSET rrFireText3D, 00EBD5A0h ret rr_render_loading: mov eax, rr3d_timer xor edx, edx mov ecx, 450 div ecx mov ebx, 1 cmp eax, rr3d_bullets jae @F cmp edx, 180 jb @F cmp edx, 390 jae @F mov ebx, 7 @@: push eax push edx invoke DrawRouletteSprite3D, ebx, 121, 112, 170, 112, 0 call RenderRussianCylinder3D pop edx pop eax cmp eax, rr3d_bullets jae rr_loading_label mov eax, edx imul eax, 41 xor edx, edx mov ecx, 450 div ecx mov edx, 156 sub edx, eax invoke DrawRouletteSprite3D, 3, 251, edx, 32, 18, 128 rr_loading_label: invoke DrawString8x8, 100, 192, OFFSET rrLoadText3D, 00EBD5A0h ret rr_render_spin: mov eax, 1400 sub eax, rr3d_timer imul eax, eax shl eax, 1 xor edx, edx mov ecx, 1400 div ecx mov angle, eax invoke DrawRouletteSprite3D, 0, 160, 113, 160, 105, angle invoke DrawString8x8, 92, 192, OFFSET rrSpinText3D, 00EBD5A0h ret rr_render_raise: mov eax, rr3d_timer imul eax, 15 xor edx, edx mov ecx, 750 div ecx mov ebx, 114 sub ebx, eax mov poseY, ebx mov poseX, 164 invoke DrawRouletteSprite3D, 6, 67, 115, 91, 119, 0 invoke DrawRouletteSprite3D, 4, poseX, poseY, 174, 114, 0 jmp rr_render_turn_label rr_render_fire: invoke DrawRouletteSprite3D, 6, 67, 115, 91, 119, 0 mov eax, 4 cmp rr3d_shot, 0 je @F mov eax, 5 @@: invoke DrawRouletteSprite3D, eax, 164, 99, 174, 114, 0 cmp rr3d_shot, 0 je rr_render_done invoke DrawString8x8, 132, 192, OFFSET rrBangText3D, 00FF9864h ret rr_render_dry: invoke DrawRouletteSprite3D, 4, 170, 114, 174, 114, 0 invoke DrawString8x8, 108, 192, OFFSET rrClickText3D, 00B6DDD0h ret rr_render_turn_label: mov eax, OFFSET rrPlayerText3D cmp rr3d_turn, 0 je @F mov eax, OFFSET rrDealerText3D @@: invoke DrawString8x8, 125, 192, eax, 00EBD5A0h ret rr_render_win: invoke DrawRouletteSprite3D, 0, 160, 114, 165, 104, 0 invoke DrawString8x8, 80, 188, OFFSET rrVictoryText3D, 003CFF3Ch invoke FormatNumber, rr3d_payout, OFFSET hudNumBuffer invoke DrawString8x8, 160, 188, OFFSET hudNumBuffer, 003CFF3Ch invoke DrawString8x8, 88, 216, OFFSET rrRoundText3D, 00EBD5A0h ret rr_render_glitch: invoke DrawRouletteSprite3D, 4, 164, 99, 174, 114, 0 invoke RenderRussianGlitch3D, 190 ret rr_render_dark: invoke DrawBox, 0, 0, 320, 240, 0 ret rr_render_gman: invoke DrawBox, 0, 0, 320, 240, 0004090Ch mov eax, rr3d_timer xor edx, edx mov ecx, 180 div ecx and eax, 7 add eax, 8 mov esi, eax mov eax, rr3d_timer xor edx, edx mov ecx, 120 div ecx add eax, 180 mov scale, eax invoke DrawRouletteSprite3D, esi, 160, 110, scale, scale, 0 invoke DrawBox, 0, 220, 320, 20, 0004090Ch invoke DrawString8x8, 52, 227, OFFSET rrWakeText3D, 00D7DFE3h cmp rr3d_timer, 400 jae rr_render_done invoke RenderRussianGlitch3D, 48 ret rr_render_return: call RenderRussianHall3D mov eax, 700 sub eax, rr3d_timer imul eax, 170 xor edx, edx mov ecx, 700 div ecx invoke RenderRussianGlitch3D, eax ret rr_render_hall: call RenderRussianHall3D rr_render_done: call RenderRussianExit3D ret RenderRussianGame3D ENDP include src_graphics\russian_party.inc