InitNativeDurak3D PROC call LoadCardTableAssets3D mov cardExitActive3D, 0 mov dkState3D, DK_IDLE mov dkStake3D, 0 mov dkError3D, 0 mov durakPlayerCnt, 0 mov durakAICnt, 0 mov durakDeckLeft, 0 mov dkPendingCard3D, 0FFFFFFFFh call ClearDurakTable call ClearKeys3D ret InitNativeDurak3D ENDP ; Removing ranks 2..5 from a uniformly shuffled deck preserves uniformity. ; The exposed trump remains in the talon and is dealt last. StartNativeDurak3D PROC uses ebx esi edi cmp dkState3D, DK_IDLE je @F cmp dkState3D, DK_RESULT jne dk_start_done @@: mov eax, dkBet3D cmp bankroll, eax jb dk_start_no_cash sub bankroll, eax mov dkStake3D, eax invoke ShuffleCards3D, OFFSET dkDeck3D xor esi, esi xor edi, edi dk_compact: mov ebx, dkDeck3D[esi*4] invoke DurakCardRank, ebx cmp eax, 4 jb @F mov dkDeck3D[edi*4], ebx inc edi @@: inc esi cmp esi, 52 jb dk_compact mov eax, dkDeck3D[35*4] mov durakTrumpCard, eax invoke DurakCardSuit, eax mov durakTrumpSuit, eax mov durakPlayerCnt, 0 mov durakAICnt, 0 mov dkCursor3D, 0 mov durakDeckLeft, 36 mov dkDiscardCount3D, 0 mov dkBouts3D, 0 mov dkError3D, 0 mov dkSelection3D, 0 mov dkPage3D, 0 mov dkTaking3D, 0 mov dkPendingCard3D, 0FFFFFFFFh call ClearDurakTable xor esi, esi dk_deal_hands: mov eax, dkDeck3D[esi*8] invoke AddDurakCard, OFFSET durakPlayerHand, OFFSET durakPlayerCnt, eax mov eax, dkDeck3D[esi*8+4] invoke AddDurakCard, OFFSET durakAIHand, OFFSET durakAICnt, eax inc esi cmp esi, 6 jb dk_deal_hands mov dkCursor3D, 12 mov durakDeckLeft, 24 ; First attacker: holder of the lowest trump, random if neither has one. mov edi, 0FFFFFFFFh xor ebx, ebx xor esi, esi dk_low_trump: mov eax, durakPlayerHand[esi*4] cmp ebx, 0 je @F mov eax, durakAIHand[esi*4] @@: push eax invoke DurakCardSuit, eax pop edx cmp eax, durakTrumpSuit jne dk_low_next cmp edx, edi jae dk_low_next mov edi, edx mov dkAttacker3D, ebx dk_low_next: inc esi cmp esi, 6 jb dk_low_trump xor esi, esi inc ebx cmp ebx, 2 jb dk_low_trump cmp edi, 0FFFFFFFFh jne @F invoke RandomRange3D, 2 mov dkAttacker3D, eax @@: mov eax, dkAttacker3D mov dkActor3D, eax mov dkTimer3D, 0 mov dkState3D, DK_DEAL call ClearKeys3D invoke PlayCasinoSound3D, OFFSET soundCards, 0, 1 call SaveProgress3D dk_start_done: ret dk_start_no_cash: mov dkError3D, 1 ret StartNativeDurak3D ENDP NativeDurakSettle3D PROC result:DWORD cmp dkState3D, DK_RESULT je dk_settle_done mov dkState3D, DK_RESULT mov eax, result mov dkOutcome3D, eax cmp eax, 1 je dk_settle_loss mov eax, dkStake3D cmp result, 2 je @F shl eax, 1 @@: add bankroll, eax invoke PlayCasinoSound3D, OFFSET soundRoundWin, 0, 1 jmp dk_settle_save dk_settle_loss: invoke PlayCasinoSound3D, OFFSET soundRoundLose, 0, 1 dk_settle_save: call ClearKeys3D xor eax,eax cmp dkOutcome3D,0 sete al invoke NpcRecordRound3D,5,eax call SaveProgress3D dk_settle_done: ret NativeDurakSettle3D ENDP DurakClampSelection3D PROC mov eax, durakPlayerCnt test eax, eax jz dk_select_zero cmp dkSelection3D, eax jb dk_select_page dec eax mov dkSelection3D, eax dk_select_page: mov eax, dkSelection3D shr eax, 3 shl eax, 3 mov dkPage3D, eax ret dk_select_zero: mov dkSelection3D, 0 mov dkPage3D, 0 ret DurakClampSelection3D ENDP DurakMoveSelection3D PROC amount:SDWORD cmp durakPlayerCnt, 0 je dk_move_done mov eax, dkSelection3D add eax, amount jns @F mov eax, durakPlayerCnt dec eax @@: cmp eax, durakPlayerCnt jb @F xor eax, eax @@: mov dkSelection3D, eax call DurakClampSelection3D dk_move_done: ret DurakMoveSelection3D ENDP DurakBeginBout3D PROC call ClearDurakTable mov eax, dkAttacker3D mov dkOriginal3D, eax mov dkActor3D, eax xor eax, 1 mov ecx, durakPlayerCnt test eax, eax jz @F mov ecx, durakAICnt @@: cmp ecx, 6 jbe @F mov ecx, 6 @@: mov dkLimit3D, ecx mov dkTaking3D, 0 mov dkState3D, DK_TURN mov dkTimer3D, 0 mov dkError3D, 0 inc dkBouts3D call DurakClampSelection3D call ClearKeys3D ret DurakBeginBout3D ENDP DurakCanTransfer3D PROC uses ebx actor:DWORD,card:DWORD cmp dkVariant3D, 0 je dk_transfer_no cmp dkTaking3D, 0 jne dk_transfer_no mov eax, actor cmp eax, dkAttacker3D je dk_transfer_no cmp durakTableCnt, 0 je dk_transfer_no cmp durakTableCnt, 6 jae dk_transfer_no call DurakTableHasDefense test eax, eax jnz dk_transfer_no invoke DurakCardRank, card mov ebx, eax invoke DurakCardRank, durakAttackCards[0] cmp eax, ebx jne dk_transfer_no mov eax, durakPlayerCnt cmp actor, 1 je @F mov eax, durakAICnt @@: cmp eax, durakTableCnt jbe dk_transfer_no mov eax, 1 ret dk_transfer_no: xor eax, eax ret DurakCanTransfer3D ENDP DurakCanPlay3D PROC uses ebx esi actor:DWORD,index:DWORD,kind:DWORD cmp dkState3D, DK_TURN jne dk_can_no mov eax, actor cmp eax, dkActor3D jne dk_can_no mov esi, OFFSET durakPlayerHand mov ecx, durakPlayerCnt test eax, eax jz @F mov esi, OFFSET durakAIHand mov ecx, durakAICnt @@: mov eax, index cmp eax, ecx jae dk_can_no mov ebx, [esi+eax*4] cmp kind, DK_TRANSFER jne @F invoke DurakCanTransfer3D, actor, ebx ret @@: cmp kind, DK_DEFEND jne dk_can_attack cmp dkTaking3D, 0 jne dk_can_no mov eax, actor cmp eax, dkAttacker3D je dk_can_no call DurakFirstUnbeaten cmp eax, 0FFFFFFFFh je dk_can_no invoke CanDurakBeat, durakAttackCards[eax*4], ebx ret dk_can_attack: cmp kind, DK_ATTACK jne dk_can_no mov eax, actor cmp eax, dkAttacker3D jne dk_can_no mov eax, durakTableCnt cmp eax, dkLimit3D jae dk_can_no test eax, eax jz dk_can_yes invoke DurakRankOnTable, ebx ret dk_can_yes: mov eax, 1 ret dk_can_no: xor eax, eax ret DurakCanPlay3D ENDP ; A card has one owner even while moving: removed from the hand, committed ; to its table slot only when this nonblocking flight finishes. DurakPlay3D PROC uses ebx esi actor:DWORD,index:DWORD,kind:DWORD invoke DurakCanPlay3D, actor, index, kind test eax, eax jz dk_play_invalid mov eax, actor mov dkPendingActor3D, eax mov eax, kind mov dkPendingKind3D, eax mov eax, durakTableCnt cmp kind, DK_DEFEND jne @F call DurakFirstUnbeaten @@: mov dkPendingSlot3D, eax imul eax, 40 add eax, 12 mov dkTargetY3D, 98 cmp kind, DK_DEFEND jne @F add eax, 6 mov dkTargetY3D, 106 @@: mov dkTargetX3D, eax mov eax, index and eax, 7 imul eax, 35 add eax, 20 mov dkPendingX3D, eax mov dkPendingY3D, 168 cmp actor, 0 je dk_play_player mov dkPendingX3D, 144 mov dkPendingY3D, 48 invoke RemoveDurakCard, OFFSET durakAIHand, OFFSET durakAICnt, index jmp dk_play_removed dk_play_player: invoke RemoveDurakCard, OFFSET durakPlayerHand, OFFSET durakPlayerCnt, index dk_play_removed: mov dkPendingCard3D, eax mov dkState3D, DK_FLIGHT mov dkTimer3D, 0 mov dkError3D, 0 call DurakClampSelection3D call ClearKeys3D invoke PlayCasinoSound3D, OFFSET soundCards, 0, 1 mov eax, 1 ret dk_play_invalid: mov dkError3D, 2 xor eax, eax ret DurakPlay3D ENDP DurakCommitFlight3D PROC uses ebx cmp dkState3D, DK_FLIGHT jne dk_commit_done mov eax, dkPendingCard3D cmp dkPendingKind3D, DK_DEFEND jne dk_commit_attack mov ebx, dkPendingSlot3D mov durakDefenseCards[ebx*4], eax jmp dk_commit_turn dk_commit_attack: call AddDurakAttack cmp dkPendingKind3D, DK_TRANSFER jne dk_commit_turn mov eax, dkPendingActor3D mov dkAttacker3D, eax mov ecx, durakPlayerCnt test eax, eax jnz @F mov ecx, durakAICnt @@: cmp ecx, 6 jbe @F mov ecx, 6 @@: mov dkLimit3D, ecx dk_commit_turn: mov dkPendingCard3D, 0FFFFFFFFh mov eax, dkAttacker3D cmp dkTaking3D, 0 jne dk_commit_actor push eax call DurakAllDefended test eax, eax pop eax jnz dk_commit_actor xor eax, 1 dk_commit_actor: mov dkActor3D, eax mov dkState3D, DK_TURN mov dkTimer3D, 0 call ClearKeys3D dk_commit_done: ret DurakCommitFlight3D ENDP DurakTake3D PROC actor:DWORD cmp dkState3D, DK_TURN jne dk_take_done mov eax, actor cmp eax, dkActor3D jne dk_take_done cmp eax, dkAttacker3D je dk_take_done cmp dkTaking3D, 0 jne dk_take_done cmp durakTableCnt, 0 je dk_take_done mov dkTaking3D, 1 mov eax, dkAttacker3D mov dkActor3D, eax mov dkTimer3D, 0 mov dkError3D, 0 call ClearKeys3D dk_take_done: ret DurakTake3D ENDP DurakFinish3D PROC actor:DWORD cmp dkState3D, DK_TURN jne dk_finish_done mov eax, actor cmp eax, dkActor3D jne dk_finish_done cmp eax, dkAttacker3D jne dk_finish_done cmp durakTableCnt, 0 je dk_finish_done cmp dkTaking3D, 0 jne dk_finish_sweep call DurakAllDefended test eax, eax jz dk_finish_done dk_finish_sweep: mov dkState3D, DK_SWEEP mov dkTimer3D, 0 mov dkError3D, 0 call ClearKeys3D dk_finish_done: ret DurakFinish3D ENDP DurakRefill3D PROC uses ebx esi actor:DWORD mov ebx, OFFSET durakPlayerCnt mov esi, OFFSET durakPlayerHand cmp actor, 0 je @F mov ebx, OFFSET durakAICnt mov esi, OFFSET durakAIHand @@: dk_refill_loop: cmp DWORD PTR [ebx], 6 jae dk_refill_done cmp dkCursor3D, 36 jae dk_refill_done mov eax, dkCursor3D mov eax, dkDeck3D[eax*4] invoke AddDurakCard, esi, ebx, eax inc dkCursor3D dec durakDeckLeft jmp dk_refill_loop dk_refill_done: ret DurakRefill3D ENDP DurakResolveBout3D PROC uses ebx esi cmp dkState3D, DK_SWEEP jne dk_resolve_done cmp dkTaking3D, 0 je dk_discard cmp dkAttacker3D, 0 je dk_collect_ai invoke CollectDurakTable, OFFSET durakPlayerHand, OFFSET durakPlayerCnt jmp dk_bout_refill dk_collect_ai: invoke CollectDurakTable, OFFSET durakAIHand, OFFSET durakAICnt jmp dk_bout_refill dk_discard: xor esi, esi mov ebx, dkDiscardCount3D dk_discard_pair: cmp esi, durakTableCnt jae dk_discard_done mov eax, durakAttackCards[esi*4] mov dkDiscard3D[ebx*4], eax inc ebx mov eax, durakDefenseCards[esi*4] mov dkDiscard3D[ebx*4], eax inc ebx inc esi jmp dk_discard_pair dk_discard_done: mov dkDiscardCount3D, ebx call ClearDurakTable dk_bout_refill: invoke DurakRefill3D, dkOriginal3D mov eax, dkOriginal3D xor eax, 1 invoke DurakRefill3D, eax call CheckDurakEnd test eax, eax jz dk_continue_bout dec eax ; legacy 1=win, 2=loss, 3=draw invoke NativeDurakSettle3D, eax ret dk_continue_bout: ; A defensive turn that takes the pile is skipped. cmp dkTaking3D, 0 jne @F xor dkAttacker3D, 1 @@: ; A pathological repeating take cycle is a push, not an arbitrary loss. cmp dkBouts3D, 300 jb @F invoke NativeDurakSettle3D, 2 ret @@: call DurakBeginBout3D dk_resolve_done: ret DurakResolveBout3D ENDP DurakCardValue3D PROC uses ebx card:DWORD invoke DurakCardRank, card mov ebx, eax invoke DurakCardSuit, card cmp eax, durakTrumpSuit jne @F add ebx, 32 @@: mov eax, ebx ret DurakCardValue3D ENDP ; AI sees only its own cards, public table/trump and the defender's count. ; This helper also serves the deterministic regression player's legal moves. DurakFindMove3D PROC uses ebx esi edi actor:DWORD,kind:DWORD local bestValue:DWORD,bestIndex:DWORD,handPtr:DWORD,handCount:DWORD mov bestValue, 0FFFFFFFFh mov bestIndex, 0FFFFFFFFh mov handPtr, OFFSET durakPlayerHand mov eax, durakPlayerCnt cmp actor, 0 je @F mov handPtr, OFFSET durakAIHand mov eax, durakAICnt @@: mov handCount, eax xor esi, esi dk_find_card: cmp esi, handCount jae dk_find_done invoke DurakCanPlay3D, actor, esi, kind test eax, eax jz dk_find_next mov ebx, handPtr invoke DurakCardValue3D, DWORD PTR [ebx+esi*4] cmp eax, bestValue jae dk_find_next mov bestValue, eax mov bestIndex, esi dk_find_next: inc esi jmp dk_find_card dk_find_done: mov eax, bestIndex ret DurakFindMove3D ENDP DurakAITurn3D PROC cmp dkAttacker3D, 1 je dk_ai_attack invoke DurakFindMove3D, 1, DK_TRANSFER cmp eax, 0FFFFFFFFh je @F invoke DurakPlay3D, 1, eax, DK_TRANSFER ret @@: invoke DurakFindMove3D, 1, DK_DEFEND cmp eax, 0FFFFFFFFh je dk_ai_take invoke DurakPlay3D, 1, eax, DK_DEFEND ret dk_ai_take: invoke DurakTake3D, 1 ret dk_ai_attack: invoke DurakFindMove3D, 1, DK_ATTACK cmp eax, 0FFFFFFFFh je dk_ai_finish invoke DurakPlay3D, 1, eax, DK_ATTACK ret dk_ai_finish: invoke DurakFinish3D, 1 ret DurakAITurn3D ENDP DurakMouse3D PROC uses ebx cmp mouseClick3D, 0 je dk_mouse_done mov mouseClick3D, 0 cmp mouseY3D, 3 jb dk_mouse_done cmp mouseY3D, 17 jae @F cmp mouseX3D, 252 jb dk_mouse_done cmp mouseX3D, 312 jae dk_mouse_done call RequestCardExit3D ret @@: cmp dkState3D, DK_IDLE je dk_mouse_ready cmp dkState3D, DK_RESULT je dk_mouse_ready cmp dkState3D, DK_TURN jne dk_mouse_done cmp dkActor3D, 0 jne dk_mouse_done cmp mouseY3D, 168 jb dk_mouse_actions cmp mouseY3D, 216 jae dk_mouse_actions cmp mouseX3D, 16 jb dk_mouse_prev cmp mouseX3D, 304 jae dk_mouse_next mov eax, mouseX3D sub eax, 20 js dk_mouse_done xor edx, edx mov ecx, 35 div ecx cmp edx, 34 jae dk_mouse_done cmp eax, 8 jae dk_mouse_done add eax, dkPage3D cmp eax, durakPlayerCnt jae dk_mouse_done mov dkSelection3D, eax ret dk_mouse_prev: cmp durakPlayerCnt, DK_PAGE_SIZE jbe dk_mouse_done cmp mouseX3D, 2 jb dk_mouse_done cmp mouseY3D, 180 jb dk_mouse_done cmp mouseY3D, 200 jae dk_mouse_done mov pressedKeys3D[21h], 1 ret dk_mouse_next: cmp durakPlayerCnt, DK_PAGE_SIZE jbe dk_mouse_done cmp mouseX3D, 318 jae dk_mouse_done cmp mouseY3D, 180 jb dk_mouse_done cmp mouseY3D, 200 jae dk_mouse_done mov pressedKeys3D[22h], 1 ret dk_mouse_actions: cmp mouseY3D, 222 jb dk_mouse_done cmp mouseY3D, 238 jae dk_mouse_done mov eax, mouseX3D mov ecx, 0Dh cmp eax, 8 jb dk_mouse_done cmp eax, 78 jb dk_mouse_key mov ecx, 42h cmp eax, 86 jb dk_mouse_done cmp eax, 156 jb dk_mouse_key mov ecx, 54h cmp eax, 164 jb dk_mouse_done cmp eax, 234 jb dk_mouse_key mov ecx, 44h cmp eax, 242 jb dk_mouse_done cmp eax, 312 jb dk_mouse_key ret dk_mouse_ready: cmp mouseY3D, 222 jb dk_mouse_bet cmp mouseY3D, 238 jae dk_mouse_done cmp mouseX3D, 8 jb dk_mouse_done cmp mouseX3D, 118 jb dk_mouse_variant cmp mouseX3D, 125 jb dk_mouse_done cmp mouseX3D, 195 jae dk_mouse_done mov ecx, 20h jmp dk_mouse_key dk_mouse_variant: mov ecx, 54h jmp dk_mouse_key dk_mouse_bet: cmp mouseY3D, 199 jb dk_mouse_done cmp mouseY3D, 215 jae dk_mouse_done cmp mouseX3D, 11 jb dk_mouse_done mov ecx, 28h cmp mouseX3D, 29 jb dk_mouse_key cmp mouseX3D, 35 jb dk_mouse_done cmp mouseX3D, 53 jae dk_mouse_done mov ecx, 26h dk_mouse_key: mov pressedKeys3D[ecx], 1 dk_mouse_done: ret DurakMouse3D ENDP HandleNativeDurak3D PROC uses ebx call HandleCardExit3D test eax, eax jnz dk_input_done call DurakMouse3D cmp cardExitActive3D, 0 jne dk_input_done cmp engineMode, MODE_DURAK jne dk_input_done cmp dkState3D, DK_IDLE je dk_ready_input cmp dkState3D, DK_RESULT je dk_ready_input mov eax, deltaTime3D add dkTimer3D, eax cmp dkState3D, DK_DEAL jne @F cmp dkTimer3D, TABLE_FLIGHT_MS*12 jb dk_input_done call DurakBeginBout3D ret @@: cmp dkState3D, DK_FLIGHT jne @F cmp dkTimer3D, TABLE_FLIGHT_MS jb dk_input_done call DurakCommitFlight3D ret @@: cmp dkState3D, DK_SWEEP jne @F cmp dkTimer3D, 600 jb dk_input_done call DurakResolveBout3D ret @@: cmp dkState3D, DK_TURN jne dk_input_done cmp dkActor3D, 0 je dk_player_input cmp dkTimer3D, 650 jb dk_input_done call DurakAITurn3D ret dk_player_input: invoke Pressed3D, 25h test eax, eax jz @F invoke DurakMoveSelection3D, -1 @@: invoke Pressed3D, 27h test eax, eax jz @F invoke DurakMoveSelection3D, 1 @@: invoke Pressed3D, 21h test eax, eax jz @F invoke DurakMoveSelection3D, -8 @@: invoke Pressed3D, 22h test eax, eax jz @F invoke DurakMoveSelection3D, 8 @@: mov ebx, 31h dk_number_select: invoke Pressed3D, ebx test eax, eax jz @F mov eax, ebx sub eax, 31h add eax, dkPage3D cmp eax, durakPlayerCnt jae @F mov dkSelection3D, eax @@: inc ebx cmp ebx, 39h jb dk_number_select invoke Pressed3D, 42h test eax, eax jz @F invoke DurakTake3D, 0 ret @@: invoke Pressed3D, 44h test eax, eax jz @F invoke DurakFinish3D, 0 ret @@: invoke Pressed3D, 54h test eax, eax jz @F invoke DurakPlay3D, 0, dkSelection3D, DK_TRANSFER ret @@: invoke Pressed3D, 0Dh test eax, eax jnz dk_player_play invoke Pressed3D, 20h test eax, eax jz dk_input_done dk_player_play: mov eax, DK_ATTACK cmp dkAttacker3D, 0 je @F mov eax, DK_DEFEND @@: invoke DurakPlay3D, 0, dkSelection3D, eax ret dk_ready_input: invoke Pressed3D, 54h test eax, eax jz @F xor dkVariant3D, 1 @@: invoke Pressed3D, 26h test eax, eax jz @F cmp dkBet3D, 10000 jae @F add dkBet3D, 100 @@: invoke Pressed3D, 28h test eax, eax jz @F cmp dkBet3D, 100 jbe @F sub dkBet3D, 100 @@: invoke Pressed3D, 20h test eax, eax jnz dk_ready_start invoke Pressed3D, 0Dh test eax, eax jz dk_input_done dk_ready_start: call StartNativeDurak3D dk_input_done: ret HandleNativeDurak3D ENDP