KAZIK/src_graphics/native_durak.inc
Artvasil09 3ad5bf4c9d Develop assembly-only casino scenes and native games
Add native Durak, poker, blackjack, roulette, Buckshot and billiards scenes with animation assets and regression tests. Improve arrival, city, hall, profiles, shops, audio and exit handling; remove the portable C game core and refresh shipping executables.
2026-10-08 22:39:19 +03:00

872 lines
18 KiB
PHP

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
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