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.
1005 lines
28 KiB
PHP
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
|