KAZIK/src_graphics/npc_mind.inc
Artvasil09 881a0061dc Add native NPC visitors with contracts, Clover slots, Pyramid, and party table scenes
Implement 32x32 BFS visitor navigation and daily contracts system with companion .npc journals; add native assembly scenes for Clover slots, Pyramid cards, Blackjack Party and Russian Party; package animation and sprite packs; add comprehensive offline test suites and rebuild shipping binaries.
2026-10-13 09:47:33 +03:00

1005 lines
28 KiB
PHP

; ==============================================================================
; NPC MIND CODE - MASM32 / CP1251
; Logic, Pathfinding Steering, Personality Thinking & Multi-Seat Poker Engine
; ==============================================================================
.code
; ------------------------------------------------------------------------------
; InitCasinoNpcMind: Èíèöèàëèçàöèÿ âñåõ 6 ïåðñîíàæåé è èõ ñòàðòîâûõ òî÷åê
; ------------------------------------------------------------------------------
InitCasinoNpcMind PROC uses ebx esi edi
xor esi, esi
npc_init_loop:
mov eax, esi
imul eax, SIZEOF NpcMindEntity
lea edi, casinoNpcs3D[eax]
; Ïðèâÿçêà ê ïðîôèëþ (0..5)
mov [edi].NpcMindEntity.profileId, esi
; Çàäàåì ñòàðòîâûå êîîðäèíàòû ó ðàçíûõ âåéïîèíòîâ
; NPC 0 (Àêóëà) -> ó ñòîëà ïîêåðà (óçåë 12)
; NPC 1 (Ëóäîìàí) -> ó àâòîìàòîâ (óçåë 5)
; NPC 2 (Ñêàëà) -> ó áëýêäæåêà (óçåë 10)
; NPC 3 (Àâòîîòâåò÷èê) -> ó áàðà (óçåë 7)
; NPC 4 (Ìàæîð) -> ó ñöåíû (óçåë 3)
; NPC 5 (Íîâè÷îê) -> ó âõîäà (óçåë 0)
mov eax, esi
cmp eax, 0
jne @F
mov ebx, 12
jmp set_start_node
@@:
cmp eax, 1
jne @F
mov ebx, 5
jmp set_start_node
@@:
cmp eax, 2
jne @F
mov ebx, 10
jmp set_start_node
@@:
cmp eax, 3
jne @F
mov ebx, 7
jmp set_start_node
@@:
cmp eax, 4
jne @F
mov ebx, 3
jmp set_start_node
@@:
mov ebx, 0
set_start_node:
mov [edi].NpcMindEntity.currentNode, ebx
mov [edi].NpcMindEntity.targetNode, ebx
imul ebx, SIZEOF NpcWaypoint
mov eax, casinoWaypoints3D[ebx].posX
mov [edi].NpcMindEntity.posX, eax
mov eax, casinoWaypoints3D[ebx].posY
mov [edi].NpcMindEntity.posY, eax
mov [edi].NpcMindEntity.speed, (FP_1 / 18) ; Ñêîðîñòü õîäüáû
mov [edi].NpcMindEntity.state, NPCAI_STATE_IDLE
mov [edi].NpcMindEntity.currentTilt, 0
; Ñòàðòîâûå ôèøêè èç ïðîôèëÿ
mov eax, esi
imul eax, SIZEOF NpcPersonality
mov ebx, casinoNpcProfiles[eax].defaultBank
mov [edi].NpcMindEntity.currentChips, ebx
; Ñòàðòîâûé òàéìåð è ìûñëè
invoke RandomRange, 60
add eax, 40
mov [edi].NpcMindEntity.stateTimer, eax
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thIdleBored
mov [edi].NpcMindEntity.reactionPtr, 0
inc esi
cmp esi, NPCAI_MAX_NPCS
jb npc_init_loop
ret
InitCasinoNpcMind ENDP
; ------------------------------------------------------------------------------
; UpdateCasinoNpcMind: Ïîêàäðîâîå îáíîâëåíèå ïîâåäåíèÿ è ïåðåìåùåíèÿ NPC â çàëå
; ------------------------------------------------------------------------------
UpdateCasinoNpcMind PROC uses ebx esi edi
local nextX:DWORD, nextY:DWORD, dxFP:DWORD, dyFP:DWORD
xor esi, esi
npc_update_loop:
mov eax, esi
imul eax, SIZEOF NpcMindEntity
lea edi, casinoNpcs3D[eax]
; 1. Ïëàâíîå îñòûâàíèå òèëüòà ñî âðåìåíåì
mov eax, [edi].NpcMindEntity.currentTilt
test eax, eax
jz @F
dec [edi].NpcMindEntity.currentTilt
@@:
; 2. Ìàøèíà ñîñòîÿíèé
mov eax, [edi].NpcMindEntity.state
cmp eax, NPCAI_STATE_IDLE
je npc_handle_idle
cmp eax, NPCAI_STATE_WALK
je npc_handle_walk
jmp npc_handle_activity
npc_handle_idle:
dec [edi].NpcMindEntity.stateTimer
jnz npc_next_entity
; Âðåìÿ îæèäàíèÿ èñòåêëî -> âûáèðàåì íîâóþ öåëü
; Åñëè ôèøåê ìàëî (< 500) -> èäåì ê áàíêîìàòó
cmp [edi].NpcMindEntity.currentChips, 500
jge @F
mov [edi].NpcMindEntity.targetNode, 14
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thWalkAtm
jmp start_walking
@@:
; Èíà÷å âûáèðàåì ñëó÷àéíûé óçåë íàçíà÷åíèÿ
invoke RandomRange, NPCAI_WAYPOINT_COUNT
mov [edi].NpcMindEntity.targetNode, eax
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thWalkWander
start_walking:
mov [edi].NpcMindEntity.state, NPCAI_STATE_WALK
jmp npc_next_entity
npc_handle_activity:
dec [edi].NpcMindEntity.stateTimer
jnz npc_next_entity
; Àêòèâíîñòü çàêîí÷åíà (ïîèãðàë, âûïèë, ñíÿë äåíüãè)
mov [edi].NpcMindEntity.state, NPCAI_STATE_IDLE
invoke RandomRange, 50
add eax, 40
mov [edi].NpcMindEntity.stateTimer, eax
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thIdleBored
jmp npc_next_entity
npc_handle_walk:
; Ðàñ÷åò âåêòîðà ê öåëåâîìó âåéïîèíòó
mov ebx, [edi].NpcMindEntity.targetNode
imul ebx, SIZEOF NpcWaypoint
mov eax, casinoWaypoints3D[ebx].posX
sub eax, [edi].NpcMindEntity.posX
mov dxFP, eax
mov eax, casinoWaypoints3D[ebx].posY
sub eax, [edi].NpcMindEntity.posY
mov dyFP, eax
; Ïðîâåðêà áëèçîñòè ê öåëåâîé òî÷êå (|dx| < FP_HALF è |dy| < FP_HALF)
mov eax, dxFP
test eax, eax
jns @F
neg eax
@@:
cmp eax, (FP_HALF / 2)
jae move_towards_target
mov eax, dyFP
test eax, eax
jns @F
neg eax
@@:
cmp eax, (FP_HALF / 2)
jae move_towards_target
; Ïðèáûëè íà âåéïîèíò!
mov eax, [edi].NpcMindEntity.targetNode
mov [edi].NpcMindEntity.currentNode, eax
; Ïðîâåðÿåì òèï çîíû ýòîãî âåéïîèíòà
mov ebx, eax
imul ebx, SIZEOF NpcWaypoint
mov eax, casinoWaypoints3D[ebx].zoneType
cmp eax, 1 ; Ïîêåð
je enter_gamble
cmp eax, 2 ; Áëýêäæåê
je enter_gamble
cmp eax, 5 ; Áàð
je enter_bar
cmp eax, 6 ; Áàíêîìàò
je enter_atm
cmp eax, 7 ; Ñöåíà
je enter_stage
; Îáû÷íûé êîðèäîð -> ïàóçà è âûáîð ñëåäóþùåãî
mov [edi].NpcMindEntity.state, NPCAI_STATE_IDLE
invoke RandomRange, 40
add eax, 30
mov [edi].NpcMindEntity.stateTimer, eax
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thIdleBored
jmp npc_next_entity
enter_gamble:
mov [edi].NpcMindEntity.state, NPCAI_STATE_GAMBLE
mov [edi].NpcMindEntity.stateTimer, 120
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thWalkTable
jmp npc_next_entity
enter_bar:
mov [edi].NpcMindEntity.state, NPCAI_STATE_BAR
mov [edi].NpcMindEntity.stateTimer, 100
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thWalkBar
jmp npc_next_entity
enter_atm:
mov [edi].NpcMindEntity.state, NPCAI_STATE_ATM
mov [edi].NpcMindEntity.stateTimer, 80
add [edi].NpcMindEntity.currentChips, 3000
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thWalkAtm
jmp npc_next_entity
enter_stage:
mov [edi].NpcMindEntity.state, NPCAI_STATE_STAGE
mov [edi].NpcMindEntity.stateTimer, 150
mov [edi].NpcMindEntity.thoughtPtr, OFFSET thWalkStage
jmp npc_next_entity
move_towards_target:
; Äåëàåì øàã â íàïðàâëåíèè öåëè
mov eax, [edi].NpcMindEntity.posX
cmp dxFP, 0
jle move_left
add eax, [edi].NpcMindEntity.speed
jmp check_step_x
move_left:
sub eax, [edi].NpcMindEntity.speed
check_step_x:
mov nextX, eax
mov eax, [edi].NpcMindEntity.posY
cmp dyFP, 0
jle move_up
add eax, [edi].NpcMindEntity.speed
jmp check_step_y
move_up:
sub eax, [edi].NpcMindEntity.speed
check_step_y:
mov nextY, eax
; Ïðîâåðêà êîëëèçèé ÷åðåç CanWalk3D
invoke CanWalk3D, nextX, [edi].NpcMindEntity.posY
test eax, eax
jz @F
mov eax, nextX
mov [edi].NpcMindEntity.posX, eax
@@:
invoke CanWalk3D, [edi].NpcMindEntity.posX, nextY
test eax, eax
jz npc_next_entity
mov eax, nextY
mov [edi].NpcMindEntity.posY, eax
npc_next_entity:
inc esi
cmp esi, NPCAI_MAX_NPCS
jb npc_update_loop
ret
UpdateCasinoNpcMind ENDP
; ------------------------------------------------------------------------------
; MultiPoker_InitTable: Ïîäãîòîâêà ñòîëà íà 4 ìåñòà (Èãðîê + 3 áîòà)
; ------------------------------------------------------------------------------
MultiPoker_InitTable PROC uses ebx esi edi
lea edi, multiPokerTable
mov [edi].MultiPokerTable.commCount, 0
mov [edi].MultiPokerTable.mainPot, 0
mov [edi].MultiPokerTable.currentHighBet, 0
mov [edi].MultiPokerTable.minRaise, 100
mov [edi].MultiPokerTable.smallBlind, 50
mov [edi].MultiPokerTable.bigBlind, 100
mov [edi].MultiPokerTable.dealerButton, 0
mov [edi].MultiPokerTable.activeSeats, 4
mov [edi].MultiPokerTable.stage, MPK_STAGE_PREFLOP
; Ìåñòî 0: Èãðîê
mov [edi].MultiPokerTable.seats[0*SIZEOF MpkSeat].isHuman, 1
mov [edi].MultiPokerTable.seats[0*SIZEOF MpkSeat].profileId, 0
mov eax, bankroll
mov [edi].MultiPokerTable.seats[0*SIZEOF MpkSeat].chips, eax
mov [edi].MultiPokerTable.seats[0*SIZEOF MpkSeat].seatStatus, MPK_SEAT_ACTIVE
mov [edi].MultiPokerTable.seats[0*SIZEOF MpkSeat].lastAction, MPK_ACT_NONE
mov [edi].MultiPokerTable.seats[0*SIZEOF MpkSeat].thoughtPtr, 0
mov [edi].MultiPokerTable.seats[0*SIZEOF MpkSeat].reactionPtr, 0
; Ìåñòî 1: Âèêòîð «Àêóëà» (ïðîôèëü 0)
mov [edi].MultiPokerTable.seats[1*SIZEOF MpkSeat].isHuman, 0
mov [edi].MultiPokerTable.seats[1*SIZEOF MpkSeat].profileId, 0
mov eax, casinoNpcProfiles[0*SIZEOF NpcPersonality].defaultBank
mov [edi].MultiPokerTable.seats[1*SIZEOF MpkSeat].chips, eax
mov [edi].MultiPokerTable.seats[1*SIZEOF MpkSeat].seatStatus, MPK_SEAT_ACTIVE
mov [edi].MultiPokerTable.seats[1*SIZEOF MpkSeat].lastAction, MPK_ACT_NONE
; Ìåñòî 2: Ñåðãåé «Ëóäîìàí» (ïðîôèëü 1)
mov [edi].MultiPokerTable.seats[2*SIZEOF MpkSeat].isHuman, 0
mov [edi].MultiPokerTable.seats[2*SIZEOF MpkSeat].profileId, 1
mov eax, casinoNpcProfiles[1*SIZEOF NpcPersonality].defaultBank
mov [edi].MultiPokerTable.seats[2*SIZEOF MpkSeat].chips, eax
mov [edi].MultiPokerTable.seats[2*SIZEOF MpkSeat].seatStatus, MPK_SEAT_ACTIVE
mov [edi].MultiPokerTable.seats[2*SIZEOF MpkSeat].lastAction, MPK_ACT_NONE
; Ìåñòî 3: Äÿäÿ Òîëÿ «Ñêàëà» (ïðîôèëü 2)
mov [edi].MultiPokerTable.seats[3*SIZEOF MpkSeat].isHuman, 0
mov [edi].MultiPokerTable.seats[3*SIZEOF MpkSeat].profileId, 2
mov eax, casinoNpcProfiles[2*SIZEOF NpcPersonality].defaultBank
mov [edi].MultiPokerTable.seats[3*SIZEOF MpkSeat].chips, eax
mov [edi].MultiPokerTable.seats[3*SIZEOF MpkSeat].seatStatus, MPK_SEAT_ACTIVE
mov [edi].MultiPokerTable.seats[3*SIZEOF MpkSeat].lastAction, MPK_ACT_NONE
ret
MultiPoker_InitTable ENDP
; ------------------------------------------------------------------------------
; MultiPoker_ShuffleDeck: Çàïîëíåíèå è òàñîâêà êîëîäû èç 52 êàðò
; ------------------------------------------------------------------------------
MultiPoker_ShuffleDeck PROC uses ebx esi edi
; Çàïîëíÿåì 0..51
xor ecx, ecx
fill_deck:
mov multiDeck[ecx*4], ecx
inc ecx
cmp ecx, 52
jb fill_deck
; Òàñîâêà Ôèøåðà-Éåòñà
mov ecx, 51
shuffle_loop:
push ecx
inc ecx
invoke RandomRange, ecx
pop ecx
; Îáìåí multiDeck[ecx*4] è multiDeck[eax*4]
mov edx, multiDeck[ecx*4]
mov ebx, multiDeck[eax*4]
mov multiDeck[ecx*4], ebx
mov multiDeck[eax*4], edx
dec ecx
jnz shuffle_loop
mov multiDeckIndex, 0
ret
MultiPoker_ShuffleDeck ENDP
; ------------------------------------------------------------------------------
; MultiPoker_StartHand: Ðàçäà÷à êàðò è ñáîð áëàéíäîâ
; ------------------------------------------------------------------------------
MultiPoker_StartHand PROC uses ebx esi edi
lea edi, multiPokerTable
call MultiPoker_ShuffleDeck
mov [edi].MultiPokerTable.commCount, 0
mov [edi].MultiPokerTable.mainPot, 0
mov [edi].MultiPokerTable.currentHighBet, 0
mov [edi].MultiPokerTable.stage, MPK_STAGE_PREFLOP
; Ïåðåäâèãàåì áàòòîí äèëåðà
mov eax, [edi].MultiPokerTable.dealerButton
inc eax
and eax, 3
mov [edi].MultiPokerTable.dealerButton, eax
; Ðàçäàåì ïî 2 êàðòû êàæäîìó àêòèâíîìó ìåñòó
xor esi, esi
deal_seats_loop:
imul eax, esi, SIZEOF MpkSeat
lea ebx, [edi].MultiPokerTable.seats[eax]
; Åñëè íåò ôèøåê -> âûáûë
cmp [ebx].MpkSeat.chips, 50
jae seat_can_play
mov [ebx].MpkSeat.seatStatus, MPK_SEAT_BUST
jmp next_deal_seat
seat_can_play:
mov [ebx].MpkSeat.seatStatus, MPK_SEAT_ACTIVE
mov [ebx].MpkSeat.roundBet, 0
mov [ebx].MpkSeat.totalBet, 0
mov [ebx].MpkSeat.lastAction, MPK_ACT_NONE
; Êàðòà 1
mov edx, multiDeckIndex
mov eax, multiDeck[edx*4]
mov [ebx].MpkSeat.card1, eax
inc multiDeckIndex
; Êàðòà 2
mov edx, multiDeckIndex
mov eax, multiDeck[edx*4]
mov [ebx].MpkSeat.card2, eax
inc multiDeckIndex
next_deal_seat:
inc esi
cmp esi, MPK_MAX_SEATS
jb deal_seats_loop
; Ðàçäàåì 5 îáùèõ êàðò íà ñòîë (ïîêà ñêðûòû)
xor esi, esi
deal_comm_loop:
mov edx, multiDeckIndex
mov eax, multiDeck[edx*4]
mov [edi].MultiPokerTable.commCards[esi*4], eax
inc multiDeckIndex
inc esi
cmp esi, 5
jb deal_comm_loop
; Ñáîð Ìàëîãî Áëàéíäà
mov eax, [edi].MultiPokerTable.dealerButton
inc eax
and eax, 3
imul eax, SIZEOF MpkSeat
mov ebx, [edi].MultiPokerTable.smallBlind
sub [edi].MultiPokerTable.seats[eax].chips, ebx
mov [edi].MultiPokerTable.seats[eax].roundBet, ebx
add [edi].MultiPokerTable.mainPot, ebx
; Ñáîð Áîëüøîãî Áëàéíäà
mov eax, [edi].MultiPokerTable.dealerButton
add eax, 2
and eax, 3
imul eax, SIZEOF MpkSeat
mov ebx, [edi].MultiPokerTable.bigBlind
sub [edi].MultiPokerTable.seats[eax].chips, ebx
mov [edi].MultiPokerTable.seats[eax].roundBet, ebx
add [edi].MultiPokerTable.mainPot, ebx
mov eax, [edi].MultiPokerTable.bigBlind
mov [edi].MultiPokerTable.currentHighBet, eax
; Ïåðâûé õîä ïîñëå ÁÁ
mov eax, [edi].MultiPokerTable.dealerButton
add eax, 3
and eax, 3
mov [edi].MultiPokerTable.currentTurn, eax
ret
MultiPoker_StartHand ENDP
; ------------------------------------------------------------------------------
; MultiPoker_NpcDecideTurn: Ãëóáîêàÿ ëîãèêà ïðèíÿòèÿ ðåøåíèé ÈÈ áîòà
; Âõîä: seatIdx (0..3)
; ------------------------------------------------------------------------------
MultiPoker_NpcDecideTurn PROC uses ebx esi edi, seatIdx:DWORD
local pSeat:DWORD, pProf:DWORD
local toCall:DWORD, handScore:DWORD, potOdds:DWORD
local aggro:DWORD, bluff:DWORD, courage:DWORD, tilt:DWORD
; 1. Ïîëó÷àåì óêàçàòåëè íà ñòóë è ïðîôèëü
mov eax, seatIdx
imul eax, SIZEOF MpkSeat
lea esi, multiPokerTable.seats[eax]
mov pSeat, esi
mov eax, [esi].MpkSeat.profileId
imul eax, SIZEOF NpcPersonality
lea edi, casinoNpcProfiles[eax]
mov pProf, edi
; Ñóììà äëÿ êîëëà
mov eax, multiPokerTable.currentHighBet
sub eax, [esi].MpkSeat.roundBet
mov toCall, eax
; 2. Îöåíêà ñèëû ðóêè (0..100)
; Êàðìàííûå êàðòû
mov eax, [esi].MpkSeat.card1
mov multiEvalHand[0], eax
mov eax, [esi].MpkSeat.card2
mov multiEvalHand[4], eax
mov ecx, multiPokerTable.commCount
test ecx, ecx
jz eval_preflop_hand
; Ïîñòôëîï: êîïèðóåì îòêðûòûå êàðòû ñòîëà
xor edx, edx
copy_comm_eval:
mov eax, multiPokerTable.commCards[edx*4]
mov multiEvalHand[edx*4 + 8], eax
inc edx
cmp edx, ecx
jb copy_comm_eval
lea eax, [ecx + 2]
invoke EvaluatePokerN, OFFSET multiEvalHand, eax
; Score / 100 äàåò ïðèìåðíûé ïðîöåíò ñèëû
xor edx, edx
mov ecx, 80
div ecx
mov handScore, eax
jmp apply_multi_opponent
eval_preflop_hand:
; Ïðåôëîï ýâðèñòèêà: ñòàðøèíñòâî è êàðìàííûå ïàðû
mov eax, [esi].MpkSeat.card1
xor edx, edx
mov ecx, 13
div ecx
mov ebx, edx ; ðàíã 1 (0..12)
mov eax, [esi].MpkSeat.card2
xor edx, edx
div ecx
mov ecx, edx ; ðàíã 2 (0..12)
cmp ebx, ecx
jne preflop_ranks
; Ïàðà!
lea eax, [ebx*4 + 40]
mov handScore, eax
jmp apply_multi_opponent
preflop_ranks:
lea eax, [ebx + ecx]
imul eax, 3
add eax, 15
mov handScore, eax
apply_multi_opponent:
; Øòðàô çà èãðó ïðîòèâ íåñêîëüêèõ îïïîíåíòîâ
mov ecx, multiPokerTable.activeSeats
cmp ecx, 2
jbe @F
imul ecx, 4
sub handScore, ecx
jns @F
mov handScore, 5
@@:
; 3. Ïñèõîëîãè÷åñêèå ìîäèôèêàòîðû + ÒÈËÜÒ
; Íàõîäèì ñîîòâåòñòâóþùåãî NPC â casinoNpcs3D äëÿ ó÷åòà òåêóùåãî òèëüòà
mov eax, [esi].MpkSeat.profileId
imul eax, SIZEOF NpcMindEntity
mov eax, casinoNpcs3D[eax].currentTilt
mov tilt, eax
mov eax, [edi].NpcPersonality.baseAggression
mov edx, tilt
shr edx, 1
add eax, edx
mov aggro, eax
mov eax, [edi].NpcPersonality.baseBluffRate
add eax, tilt
mov bluff, eax
mov eax, [edi].NpcPersonality.baseCourage
mov courage, eax
; 4. Ðàñ÷åò øàíñîâ áàíêà (Pot Odds)
cmp toCall, 0
je branch_free_check
; potOdds = (toCall * 100) / (mainPot + toCall)
mov eax, toCall
imul eax, 100
mov ecx, multiPokerTable.mainPot
add ecx, toCall
test ecx, ecx
jnz @F
mov ecx, 1
@@:
xor edx, edx
div ecx
mov potOdds, eax
; Ïðîâåðêà íà áëåô ñî ñëàáîé ðóêîé
cmp handScore, 30
ja check_standard_call
invoke RandomRange, 100
cmp eax, bluff
jbe decide_bluff_raise
check_standard_call:
; Ñðàâíåíèå ñèëû ðóêè è øàíñîâ áàíêà
mov eax, handScore
add eax, courage
shr eax, 1
cmp eax, potOdds
jb decide_fold_action
; Ðóêà äîñòàòî÷íà äëÿ ïðîäîëæåíèÿ! Ïðîâåðÿåì æåëàíèå ðåéçèòü
mov eax, handScore
add eax, aggro
cmp eax, 120
jae decide_value_raise
; Ñòàíäàðòíûé Êîëë
mov [esi].MpkSeat.lastAction, MPK_ACT_CALL
mov [esi].MpkSeat.thoughtPtr, OFFSET thActCall
mov eax, [edi].NpcPersonality.phraseWinPtr
mov [esi].MpkSeat.reactionPtr, eax
ret
decide_value_raise:
mov [esi].MpkSeat.lastAction, MPK_ACT_RAISE
mov eax, multiPokerTable.minRaise
imul eax, 2
mov [esi].MpkSeat.lastRaiseAmount, eax
mov [esi].MpkSeat.thoughtPtr, OFFSET thEvalGood
mov eax, [edi].NpcPersonality.phraseBluffPtr
mov [esi].MpkSeat.reactionPtr, eax
ret
decide_bluff_raise:
mov [esi].MpkSeat.lastAction, MPK_ACT_RAISE
mov eax, multiPokerTable.minRaise
imul eax, 3
mov [esi].MpkSeat.lastRaiseAmount, eax
mov [esi].MpkSeat.thoughtPtr, OFFSET thEvalBluffing
mov eax, [edi].NpcPersonality.phraseBluffPtr
mov [esi].MpkSeat.reactionPtr, eax
ret
decide_fold_action:
mov [esi].MpkSeat.lastAction, MPK_ACT_FOLD
mov [esi].MpkSeat.seatStatus, MPK_SEAT_FOLDED
mov [esi].MpkSeat.thoughtPtr, OFFSET thActFold
mov eax, [edi].NpcPersonality.phraseLosePtr
mov [esi].MpkSeat.reactionPtr, eax
ret
branch_free_check:
; toCall == 0 -> ×åê èëè ëèäåðñêàÿ ñòàâêà
mov eax, handScore
add eax, aggro
cmp eax, 110
jae lead_bet
mov [esi].MpkSeat.lastAction, MPK_ACT_CHECK
mov [esi].MpkSeat.thoughtPtr, OFFSET thActCheck
ret
lead_bet:
mov [esi].MpkSeat.lastAction, MPK_ACT_RAISE
mov eax, multiPokerTable.minRaise
mov [esi].MpkSeat.lastRaiseAmount, eax
mov [esi].MpkSeat.thoughtPtr, OFFSET thEvalGood
mov eax, [edi].NpcPersonality.phraseBluffPtr
mov [esi].MpkSeat.reactionPtr, eax
ret
MultiPoker_NpcDecideTurn ENDP
; ------------------------------------------------------------------------------
; MultiPoker_ApplyAction: Ïðèìåíåíèå ñòàâêè, êîëëà èëè ïàñà ê ñîñòîÿíèþ ñòîëà
; ------------------------------------------------------------------------------
MultiPoker_ApplyAction PROC uses ebx esi edi, seatIdx:DWORD, act:DWORD, amount:DWORD
mov eax, seatIdx
imul eax, SIZEOF MpkSeat
lea esi, multiPokerTable.seats[eax]
cmp act, MPK_ACT_FOLD
je apply_fold
cmp act, MPK_ACT_CHECK
je apply_check
cmp act, MPK_ACT_CALL
je apply_call
cmp act, MPK_ACT_RAISE
je apply_raise
ret
apply_fold:
mov [esi].MpkSeat.seatStatus, MPK_SEAT_FOLDED
dec multiPokerTable.activeSeats
ret
apply_check:
ret
apply_call:
mov eax, multiPokerTable.currentHighBet
sub eax, [esi].MpkSeat.roundBet
cmp eax, [esi].MpkSeat.chips
jbe @F
mov eax, [esi].MpkSeat.chips
@@:
sub [esi].MpkSeat.chips, eax
add [esi].MpkSeat.roundBet, eax
add [esi].MpkSeat.totalBet, eax
add multiPokerTable.mainPot, eax
ret
apply_raise:
; Äîñòàâêà äî currentHighBet + ðàçìåð ðåéçà
mov eax, multiPokerTable.currentHighBet
sub eax, [esi].MpkSeat.roundBet
add eax, amount
cmp eax, [esi].MpkSeat.chips
jbe @F
mov eax, [esi].MpkSeat.chips
@@:
sub [esi].MpkSeat.chips, eax
add [esi].MpkSeat.roundBet, eax
add [esi].MpkSeat.totalBet, eax
add multiPokerTable.mainPot, eax
mov eax, [esi].MpkSeat.roundBet
mov multiPokerTable.currentHighBet, eax
ret
MultiPoker_ApplyAction ENDP
; ------------------------------------------------------------------------------
; MultiPoker_NextStreet: Ïåðåõîä íà ñëåäóþùóþ óëèöó òîðãîâ
; ------------------------------------------------------------------------------
MultiPoker_NextStreet PROC uses ebx esi edi
; Ñáðîñ roundBet äëÿ âñåõ ìåñò
xor ecx, ecx
reset_round_bets:
imul eax, ecx, SIZEOF MpkSeat
mov multiPokerTable.seats[eax].roundBet, 0
inc ecx
cmp ecx, MPK_MAX_SEATS
jb reset_round_bets
mov multiPokerTable.currentHighBet, 0
mov eax, multiPokerTable.stage
cmp eax, MPK_STAGE_PREFLOP
je go_flop
cmp eax, MPK_STAGE_FLOP
je go_turn
cmp eax, MPK_STAGE_TURN
je go_river
mov multiPokerTable.stage, MPK_STAGE_SHOWDOWN
ret
go_flop:
mov multiPokerTable.commCount, 3
mov multiPokerTable.stage, MPK_STAGE_FLOP
ret
go_turn:
mov multiPokerTable.commCount, 4
mov multiPokerTable.stage, MPK_STAGE_TURN
ret
go_river:
mov multiPokerTable.commCount, 5
mov multiPokerTable.stage, MPK_STAGE_RIVER
ret
MultiPoker_NextStreet ENDP
; ------------------------------------------------------------------------------
; MultiPoker_Showdown: Îïðåäåëåíèå ïîáåäèòåëÿ è ðàñ÷åò áàíêà
; ------------------------------------------------------------------------------
MultiPoker_Showdown PROC uses ebx esi edi
local bestScore:DWORD, bestSeat:DWORD
mov bestScore, 0
mov bestSeat, 0
xor esi, esi
eval_showdown_seats:
imul eax, esi, SIZEOF MpkSeat
lea ebx, multiPokerTable.seats[eax]
cmp [ebx].MpkSeat.seatStatus, MPK_SEAT_ACTIVE
jne next_showdown_seat
; Îöåíêà 7 êàðò (2 ñâîè + 5 ñòîëà)
mov eax, [ebx].MpkSeat.card1
mov multiEvalHand[0], eax
mov eax, [ebx].MpkSeat.card2
mov multiEvalHand[4], eax
xor edx, edx
copy_7_cards:
mov eax, multiPokerTable.commCards[edx*4]
mov multiEvalHand[edx*4 + 8], eax
inc edx
cmp edx, 5
jb copy_7_cards
invoke EvaluatePokerN, OFFSET multiEvalHand, 7
mov [ebx].MpkSeat.evalScore, eax
cmp eax, bestScore
jb next_showdown_seat
mov bestScore, eax
mov bestSeat, esi
next_showdown_seat:
inc esi
cmp esi, MPK_MAX_SEATS
jb eval_showdown_seats
; Íàãðàäà ïîáåäèòåëþ
mov eax, bestSeat
imul eax, SIZEOF MpkSeat
lea ebx, multiPokerTable.seats[eax]
mov edx, multiPokerTable.mainPot
add [ebx].MpkSeat.chips, edx
; Åñëè ïîáåäèë ÷åëîâåê -> îáíîâëÿåì bankroll
cmp bestSeat, 0
jne @F
mov eax, [ebx].MpkSeat.chips
mov bankroll, eax
@@:
ret
MultiPoker_Showdown ENDP
; ------------------------------------------------------------------------------
; MultiPoker_PrintStatus: Òåêñòîâûé âûâîä ñîñòîÿíèÿ ñòîëà è ìûñëåé áîòîâ
; ------------------------------------------------------------------------------
MultiPoker_PrintStatus PROC uses ebx esi edi
invoke SetColor, COLOR_TITLE
invoke PrintStr, OFFSET mpkTitleStr
call ResetColor
invoke PrintStr, OFFSET mpkPotStr
invoke PrintUInt, multiPokerTable.mainPot
invoke PrintStr, OFFSET mpkStreetStr
mov eax, multiPokerTable.stage
cmp eax, MPK_STAGE_PREFLOP
jne @F
invoke PrintStr, OFFSET strStagePreflop
jmp print_stage_done
@@:
cmp eax, MPK_STAGE_FLOP
jne @F
invoke PrintStr, OFFSET strStageFlop
jmp print_stage_done
@@:
cmp eax, MPK_STAGE_TURN
jne @F
invoke PrintStr, OFFSET strStageTurn
jmp print_stage_done
@@:
cmp eax, MPK_STAGE_RIVER
jne @F
invoke PrintStr, OFFSET strStageRiver
jmp print_stage_done
@@:
invoke PrintStr, OFFSET strStageShow
print_stage_done:
invoke PrintStr, OFFSET nl
; Âûâîä îáùèõ êàðò ñòîëà
invoke PrintStr, OFFSET mpkCommCardsStr
cmp multiPokerTable.commCount, 0
jne print_table_cards
invoke PrintStr, OFFSET strStagePreflop
invoke PrintStr, OFFSET nl
jmp print_seats_info
print_table_cards:
invoke PrintHandRow21, OFFSET multiPokerTable.commCards, multiPokerTable.commCount, 1
print_seats_info:
invoke PrintStr, OFFSET nl
; Âûâîä êàæäîãî èç 4 ìåñò
xor esi, esi
print_seats_loop:
imul eax, esi, SIZEOF MpkSeat
lea ebx, multiPokerTable.seats[eax]
; Èìÿ ìåñòà
cmp esi, 0
jne print_npc_seat_header
invoke SetColor, COLOR_MONEY
invoke PrintStr, OFFSET promptPlayerTerminal
call ResetColor
jmp print_seat_details
print_npc_seat_header:
mov eax, [ebx].MpkSeat.profileId
imul eax, SIZEOF NpcPersonality
invoke SetColor, COLOR_WARN
invoke PrintStr, casinoNpcProfiles[eax].namePtr
call ResetColor
print_seat_details:
invoke PrintStr, OFFSET dotSpaceText
invoke PrintStr, OFFSET mpkChipsPrefix
invoke PrintUInt, [ebx].MpkSeat.chips
; Ñòàòóñ ìåñòà
cmp [ebx].MpkSeat.seatStatus, MPK_SEAT_FOLDED
jne @F
invoke SetColor, COLOR_BAD
invoke PrintStr, OFFSET observerFold1Text
call ResetColor
@@:
invoke PrintStr, OFFSET nl
; Ìûñëè áîòà (åñëè åñòü)
cmp [ebx].MpkSeat.thoughtPtr, 0
je print_reaction_check
invoke PrintStr, OFFSET mpkThoughtPrefix
invoke PrintStr, [ebx].MpkSeat.thoughtPtr
invoke PrintStr, OFFSET nl
print_reaction_check:
cmp [ebx].MpkSeat.reactionPtr, 0
je next_seat_print
invoke SetColor, COLOR_CARD
invoke PrintStr, [ebx].MpkSeat.reactionPtr
call ResetColor
invoke PrintStr, OFFSET nl
next_seat_print:
inc esi
cmp esi, MPK_MAX_SEATS
jb print_seats_loop
ret
MultiPoker_PrintStatus ENDP
; ------------------------------------------------------------------------------
; GameMultiPoker: Ãëàâíàÿ òî÷êà âõîäà äëÿ êàðòî÷íîé èãðû ïðîòèâ 3 ïîñåòèòåëåé
; ------------------------------------------------------------------------------
GameMultiPoker PROC uses ebx esi edi
call ClearView
call MultiPoker_InitTable
multi_hand_loop:
call ClearView
call MultiPoker_StartHand
multi_street_loop:
call ClearView
call MultiPoker_PrintStatus
; Ïðîâåðÿåì, îñòàëñÿ ëè â èãðå õîòÿ áû 1 îïïîíåíò
cmp multiPokerTable.activeSeats, 1
jbe multi_hand_showdown
; Êðóã òîðãîâëè äëÿ êàæäîãî àêòèâíîãî ìåñòà
xor esi, esi
multi_turn_loop:
imul eax, esi, SIZEOF MpkSeat
lea ebx, multiPokerTable.seats[eax]
cmp [ebx].MpkSeat.seatStatus, MPK_SEAT_ACTIVE
jne next_turn_player
; Õîä Èãðîêà (ìåñòî 0)
cmp esi, 0
jne handle_bot_turn
invoke PrintStr, OFFSET mpkPromptStr
call ReadKey
cmp al, '1'
je player_call
cmp al, '2'
je player_raise
cmp al, '0'
je player_fold
jmp player_call
player_fold:
invoke MultiPoker_ApplyAction, 0, MPK_ACT_FOLD, 0
jmp next_turn_player
player_call:
invoke MultiPoker_ApplyAction, 0, MPK_ACT_CALL, 0
jmp next_turn_player
player_raise:
invoke MultiPoker_ApplyAction, 0, MPK_ACT_RAISE, 100
jmp next_turn_player
handle_bot_turn:
; Õîä áîòà
invoke MultiPoker_NpcDecideTurn, esi
mov eax, [ebx].MpkSeat.lastAction
mov edx, [ebx].MpkSeat.lastRaiseAmount
invoke MultiPoker_ApplyAction, esi, eax, edx
invoke Sleep, 400
next_turn_player:
inc esi
cmp esi, MPK_MAX_SEATS
jb multi_turn_loop
; Ïåðåõîä íà ñëåäóþùóþ óëèöó
cmp multiPokerTable.stage, MPK_STAGE_RIVER
jae multi_hand_showdown
call MultiPoker_NextStreet
jmp multi_street_loop
multi_hand_showdown:
call ClearView
call MultiPoker_Showdown
call MultiPoker_PrintStatus
invoke PrintStr, OFFSET mpkWaitNextStr
call ReadKey
cmp al, '0'
je multi_poker_exit
jmp multi_hand_loop
multi_poker_exit:
ret
GameMultiPoker ENDP