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.
1289 lines
45 KiB
Python
1289 lines
45 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
Generator for NPC Mind AI system:
|
||
- src_graphics/npc_mind_data.inc
|
||
- src_graphics/npc_mind.inc
|
||
Both written in CP1251 encoding.
|
||
"""
|
||
|
||
import os
|
||
|
||
data_inc_path = r"src_graphics\npc_mind_data.inc"
|
||
code_inc_path = r"src_graphics\npc_mind.inc"
|
||
|
||
DATA_CONTENT = r'''; ==============================================================================
|
||
; NPC MIND DATA - MASM32 / CP1251
|
||
; Multi-agent AI: Open-space Navigation, Personalities & Multi-seat Poker
|
||
; ==============================================================================
|
||
|
||
NPCAI_ARCHETYPE_SHARK equ 0 ; Акула (Tight-Aggressive)
|
||
NPCAI_ARCHETYPE_MANIAC equ 1 ; Лудоман (Loose-Aggressive)
|
||
NPCAI_ARCHETYPE_ROCK equ 2 ; Скала (Tight-Passive)
|
||
NPCAI_ARCHETYPE_CALLING_STATION equ 3 ; Автоответчик (Loose-Passive)
|
||
NPCAI_ARCHETYPE_WHALE equ 4 ; Мажор (Высокий банкролл, хаос)
|
||
NPCAI_ARCHETYPE_ROOKIE equ 5 ; Новичок (Ошибки, неуверенность)
|
||
|
||
NPCAI_STATE_IDLE equ 0 ; Стоит, осматривается
|
||
NPCAI_STATE_WALK equ 1 ; Идет по вейпоинтам
|
||
NPCAI_STATE_GAMBLE equ 2 ; Играет за столом
|
||
NPCAI_STATE_BAR equ 3 ; У бара
|
||
NPCAI_STATE_ATM equ 4 ; У банкомата
|
||
NPCAI_STATE_STAGE equ 5 ; Смотрит шоу
|
||
|
||
MPK_SEAT_EMPTY equ 0
|
||
MPK_SEAT_ACTIVE equ 1
|
||
MPK_SEAT_FOLDED equ 2
|
||
MPK_SEAT_ALLIN equ 3
|
||
MPK_SEAT_BUST equ 4
|
||
|
||
MPK_ACT_NONE equ 0
|
||
MPK_ACT_FOLD equ 1
|
||
MPK_ACT_CHECK equ 2
|
||
MPK_ACT_CALL equ 3
|
||
MPK_ACT_RAISE equ 4
|
||
MPK_ACT_ALLIN equ 5
|
||
|
||
MPK_STAGE_PREFLOP equ 0
|
||
MPK_STAGE_FLOP equ 1
|
||
MPK_STAGE_TURN equ 2
|
||
MPK_STAGE_RIVER equ 3
|
||
MPK_STAGE_SHOWDOWN equ 4
|
||
|
||
NPCAI_MAX_NPCS equ 6
|
||
MPK_MAX_SEATS equ 4
|
||
NPCAI_WAYPOINT_COUNT equ 16
|
||
|
||
; ------------------------------------------------------------------------------
|
||
; Структуры данных
|
||
; ------------------------------------------------------------------------------
|
||
|
||
NpcPersonality STRUCT
|
||
namePtr dd ?
|
||
titlePtr dd ?
|
||
archetypeId dd ?
|
||
baseAggression dd ? ; 0..100
|
||
baseTightness dd ? ; 0..100
|
||
baseBluffRate dd ? ; 0..100
|
||
baseCourage dd ? ; 0..100
|
||
tiltSens dd ? ; 0..100
|
||
defaultBank dd ?
|
||
phraseWinPtr dd ?
|
||
phraseLosePtr dd ?
|
||
phraseBluffPtr dd ?
|
||
phraseBustPtr dd ?
|
||
NpcPersonality ENDS
|
||
|
||
NpcMindEntity STRUCT
|
||
posX dd ? ; 16.16 FP
|
||
posY dd ? ; 16.16 FP
|
||
dirX dd ? ; 16.16 FP
|
||
dirY dd ? ; 16.16 FP
|
||
speed dd ? ; 16.16 FP
|
||
state dd ? ; NPCAI_STATE_*
|
||
stateTimer dd ?
|
||
currentNode dd ?
|
||
targetNode dd ?
|
||
profileId dd ?
|
||
currentChips dd ?
|
||
currentTilt dd ? ; 0..100
|
||
thoughtPtr dd ?
|
||
reactionPtr dd ?
|
||
NpcMindEntity ENDS
|
||
|
||
MpkSeat STRUCT
|
||
isHuman dd ? ; 1=Player, 0=NPC
|
||
profileId dd ? ; Индекс NpcPersonality (0..5)
|
||
chips dd ?
|
||
roundBet dd ?
|
||
totalBet dd ?
|
||
card1 dd ?
|
||
card2 dd ?
|
||
evalScore dd ?
|
||
seatStatus dd ? ; MPK_SEAT_*
|
||
lastAction dd ? ; MPK_ACT_*
|
||
lastRaiseAmount dd ?
|
||
thoughtPtr dd ?
|
||
reactionPtr dd ?
|
||
MpkSeat ENDS
|
||
|
||
MultiPokerTable STRUCT
|
||
seats MpkSeat MPK_MAX_SEATS dup(<>)
|
||
commCards dd 5 dup(?)
|
||
commCount dd ?
|
||
mainPot dd ?
|
||
currentHighBet dd ?
|
||
minRaise dd ?
|
||
dealerButton dd ?
|
||
currentTurn dd ?
|
||
activeSeats dd ?
|
||
smallBlind dd ?
|
||
bigBlind dd ?
|
||
stage dd ?
|
||
MultiPokerTable ENDS
|
||
|
||
NpcWaypoint STRUCT
|
||
posX dd ? ; 16.16 FP
|
||
posY dd ? ; 16.16 FP
|
||
neighbor0 dd ?
|
||
neighbor1 dd ?
|
||
neighbor2 dd ?
|
||
neighbor3 dd ?
|
||
zoneType dd ?
|
||
NpcWaypoint ENDS
|
||
|
||
; ------------------------------------------------------------------------------
|
||
; Строковые константы и описания (CP1251)
|
||
; ------------------------------------------------------------------------------
|
||
|
||
.data
|
||
|
||
; Имена и титулы 6 уникальных персонажей
|
||
npcName0 db "Виктор «Акула»",0
|
||
npcTitle0 db "Профессионал (Tight-Aggressive)",0
|
||
|
||
npcName1 db "Сергей «Лудоман»",0
|
||
npcTitle1 db "Маньяк (Loose-Aggressive, ТИЛЬТ)",0
|
||
|
||
npcName2 db "Дядя Толя «Скала»",0
|
||
npcTitle2 db "Ветеран (Tight-Passive)",0
|
||
|
||
npcName3 db "Степан «Автоответчик»",0
|
||
npcTitle3 db "Любитель (Calling Station)",0
|
||
|
||
npcName4 db "Мажор Артём",0
|
||
npcTitle4 db "Хайроллер (Whale, Безумные ставки)",0
|
||
|
||
npcName5 db "Влад «Новичок»",0
|
||
npcTitle5 db "Студент (Rookie, Неопытный)",0
|
||
|
||
; Фразы победы
|
||
npcWin0 db "«Холодный расчет и никакой магии. Банк мой.»",0
|
||
npcWin1 db "«ХА-ХА! Кто тут король казино?! Все фишки сюда!»",0
|
||
npcWin2 db "«Тише едешь — дальше будешь, молодежь.»",0
|
||
npcWin3 db "«Опа, закрылся стрит! А вы не верили!»",0
|
||
npcWin4 db "«Официант, всем шампанского за мой счет!»",0
|
||
npcWin5 db "«Ого! Неужели я правда победил?! Вот это да!»",0
|
||
|
||
; Фразы поражения / бэдбита
|
||
npcLose0 db "«Бывает. Математическая дисперсия на дистанции вернет всё.»",0
|
||
npcLose1 db "«ДА КАК ТАК-ТО?! Опять ривером переехали, сука!»",0
|
||
npcLose2 db "«Знал ведь, что надо было сбрасывать еще на флопе...»",0
|
||
npcLose3 db "«Эх, чутка не хватило до флеша... в следующий раз повезет.»",0
|
||
npcLose4 db "«Пф-ф, копейки. Сейчас сниму еще пачку в банкомате.»",0
|
||
npcLose5 db "«Я так и знал... Надо было внимательнее читать правила.»",0
|
||
|
||
; Фразы блефа / рейза
|
||
npcBluff0 db "«Твоя рука слабее. Сэкономь фишки и пасуй.»",0
|
||
npcBluff1 db "«Слабаки! Вам не по карману этот банк, двигайте пас!»",0
|
||
npcBluff2 db "«У меня отличная карта. Не советую лезть под колеса.»",0
|
||
npcBluff3 db "«А я просто хочу посмотреть следующую карту!»",0
|
||
npcBluff4 db "«Вы ставите копейки! Давайте играть по-взрослому!»",0
|
||
npcBluff5 db "«Э-э... кажется, я поднимаю ставку. Вы со мной?»",0
|
||
|
||
; Фразы банкротства
|
||
npcBust0 db "«Лимит на сегодня исчерпан. Пора делать паузу.»",0
|
||
npcBust1 db "«Всё в ноль! Бегу к банкомату, я обязан отыграться!»",0
|
||
npcBust2 db "«Пенсия кончилась... Пойду подышу на свежем воздухе.»",0
|
||
npcBust3 db "«Ой, фишки кончились... Пойду в бар за пивом.»",0
|
||
npcBust4 db "«Опять обнулился? Ерунда, сейчас сниму еще $50,000.»",0
|
||
npcBust5 db "«Кажется, на метро мне уже не хватает...»",0
|
||
|
||
; Мысли в свободном перемещении по казино
|
||
thWalkWander db "Осматривает игровой зал в поисках интересной игры",0
|
||
thWalkTable db "Направляется к покерному столу испытать удачу",0
|
||
thWalkBar db "Решил заглянуть в бар за напитком",0
|
||
thWalkAtm db "Закончились фишки, направляется к банкомату",0
|
||
thWalkStage db "Подходит к сцене посмотреть на танцовщиц",0
|
||
thIdleBored db "Стоит у колонны, подсчитывает оставшиеся деньги",0
|
||
|
||
; Мысли за покерным столом
|
||
thEvalStrong db "Чувствует натс! Готовит ловушку для оппонентов.",0
|
||
thEvalGood db "Рука сильная, отличные шансы забрать этот банк.",0
|
||
thEvalDraw db "Считает ауты: шансы банка выгодны для колла.",0
|
||
thEvalTrash db "Карты — мусор. Смысла рисковать фишками нет.",0
|
||
thEvalTilted db "[ТИЛЬТ!] Злится после проигрыша и идет на таран!",0
|
||
thEvalBluffing db "БЛЕФ! Изображает монстра и давит ставкой!",0
|
||
|
||
; Описания действий
|
||
thActFold db "Сбрасывает карты в пас",0
|
||
thActCheck db "Чекает, пропуская ход",0
|
||
thActCall db "Коллирует (уравнивает) текущую ставку",0
|
||
thActRaise db "Повышает ставку (рейз!)",0
|
||
thActAllIn db "Двигает все фишки вперед — ВА-БАНК (ОЛЛ-ИН)!",0
|
||
|
||
; Названия улиц покера
|
||
strStagePreflop db "Префлоп (Карманные карты)",0
|
||
strStageFlop db "Флоп (3 карты стола)",0
|
||
strStageTurn db "Терн (4 карты стола)",0
|
||
strStageRiver db "Ривер (5 карт стола)",0
|
||
strStageShow db "Вскрытие карт (Showdown)",0
|
||
|
||
; Тексты интерфейса мульти-покера
|
||
mpkTitleStr db "=== ТЕХАССКИЙ ХОЛДЕМ: СТОЛ НА 4 ИГРОКА ===",13,10,0
|
||
mpkPotStr db "Банк стола: $",0
|
||
mpkStreetStr db " | Раунд: ",0
|
||
mpkCommCardsStr db "Общие карты на столе: ",0
|
||
mpkPromptStr db "Ваш ход: [1] Чек/Колл | [2] Рейз | [0] Пас -> ",0
|
||
mpkWaitNextStr db 13,10,"[Нажмите любую клавишу для следующей раздачи, 0 для выхода]...",0
|
||
mpkChipsPrefix db " Стек: $",0
|
||
mpkThoughtPrefix db " [Мысли]: ",0
|
||
mpkGameOverStr db 13,10,"Раздача окончена. Победитель: ",0
|
||
mpkWinChipsStr db " выиграл банк $",0
|
||
|
||
ALIGN 4
|
||
|
||
; База профилей 6 уникальных личностей
|
||
casinoNpcProfiles NpcPersonality <OFFSET npcName0, OFFSET npcTitle0, NPCAI_ARCHETYPE_SHARK, 75, 85, 20, 85, 15, 5000, OFFSET npcWin0, OFFSET npcLose0, OFFSET npcBluff0, OFFSET npcBust0>
|
||
NpcPersonality <OFFSET npcName1, OFFSET npcTitle1, NPCAI_ARCHETYPE_MANIAC, 95, 20, 80, 90, 85, 3500, OFFSET npcWin1, OFFSET npcLose1, OFFSET npcBluff1, OFFSET npcBust1>
|
||
NpcPersonality <OFFSET npcName2, OFFSET npcTitle2, NPCAI_ARCHETYPE_ROCK, 25, 90, 5, 30, 20, 4000, OFFSET npcWin2, OFFSET npcLose2, OFFSET npcBluff2, OFFSET npcBust2>
|
||
NpcPersonality <OFFSET npcName3, OFFSET npcTitle3, NPCAI_ARCHETYPE_CALLING_STATION, 15, 30, 10, 75, 40, 3000, OFFSET npcWin3, OFFSET npcLose3, OFFSET npcBluff3, OFFSET npcBust3>
|
||
NpcPersonality <OFFSET npcName4, OFFSET npcTitle4, NPCAI_ARCHETYPE_WHALE, 70, 45, 55, 70, 50, 10000, OFFSET npcWin4, OFFSET npcLose4, OFFSET npcBluff4, OFFSET npcBust4>
|
||
NpcPersonality <OFFSET npcName5, OFFSET npcTitle5, NPCAI_ARCHETYPE_ROOKIE, 40, 50, 25, 40, 60, 2000, OFFSET npcWin5, OFFSET npcLose5, OFFSET npcBluff5, OFFSET npcBust5>
|
||
|
||
; Граф вейпоинтов казино (координаты 16.16 FP и связи)
|
||
casinoWaypoints3D NpcWaypoint <16*FP_1, 28*FP_1, 1, 4, 8, 0, 0> ; 0: Вход в казино
|
||
NpcWaypoint <16*FP_1, 20*FP_1, 0, 2, 4, 8, 0> ; 1: Центр холла (юг)
|
||
NpcWaypoint <16*FP_1, 14*FP_1, 1, 3, 10, 12, 0> ; 2: Центр холла (север)
|
||
NpcWaypoint <16*FP_1, 6*FP_1, 2, 10, 12, 3, 7> ; 3: Перед сценой танцовщиц
|
||
NpcWaypoint < 8*FP_1, 20*FP_1, 0, 1, 5, 7, 0> ; 4: Западный коридор
|
||
NpcWaypoint < 6*FP_1, 14*FP_1, 4, 6, 2, 5, 4> ; 5: Автоматы 777
|
||
NpcWaypoint < 6*FP_1, 9*FP_1, 5, 10, 2, 6, 4> ; 6: Автоматы CloverPit
|
||
NpcWaypoint < 4*FP_1, 25*FP_1, 4, 0, 7, 7, 5> ; 7: Барная стойка
|
||
NpcWaypoint <24*FP_1, 20*FP_1, 0, 1, 9, 14, 0> ; 8: Восточный коридор
|
||
NpcWaypoint <26*FP_1, 25*FP_1, 8, 0, 9, 9, 0> ; 9: Зона отдыха VIP
|
||
NpcWaypoint <12*FP_1, 9*FP_1, 2, 3, 6, 11, 2> ; 10: Стол Блэкджека
|
||
NpcWaypoint <16*FP_1, 11*FP_1, 2, 10, 12, 11, 3> ; 11: Стол Рулетки
|
||
NpcWaypoint <20*FP_1, 9*FP_1, 2, 3, 11, 13, 1> ; 12: Стол Покера (Холдем)
|
||
NpcWaypoint <24*FP_1, 9*FP_1, 12, 8, 14, 13, 1> ; 13: Стол Дурака
|
||
NpcWaypoint <26*FP_1, 15*FP_1, 8, 13, 14, 14, 6> ; 14: Банкомат (ATM)
|
||
NpcWaypoint <10*FP_1, 15*FP_1, 2, 4, 5, 10, 0> ; 15: Зона костей/черепов
|
||
|
||
; 6 активных персонажей, гуляющих по залу казино
|
||
casinoNpcs3D NpcMindEntity NPCAI_MAX_NPCS dup(<>)
|
||
|
||
; Экземпляр стола мульти-покера
|
||
multiPokerTable MultiPokerTable <>
|
||
|
||
; Колода и рабочий буфер для мульти-покера
|
||
multiDeck dd 52 dup(0)
|
||
multiDeckIndex dd 0
|
||
multiEvalHand dd 7 dup(0)
|
||
multiActionWait dd 0
|
||
'''.strip()
|
||
|
||
CODE_CONTENT = r'''; ==============================================================================
|
||
; 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
|
||
'''.strip()
|
||
|
||
print("Writing npc_mind_data.inc in CP1251...")
|
||
with open(data_inc_path, "w", encoding="cp1251", errors="replace") as f:
|
||
f.write(DATA_CONTENT + "\n")
|
||
|
||
print("Writing npc_mind.inc in CP1251...")
|
||
with open(code_inc_path, "w", encoding="cp1251", errors="replace") as f:
|
||
f.write(CODE_CONTENT + "\n")
|
||
|
||
print("Done generating NPC mind modules.")
|