# 06 — Decision Engine (não bộ tự động)

## 1. Vai trò

Nhận `GameState` snapshot (từ MemoryReader hoặc VisionModule) → xuất `ActionCommand`
cho InputController. Chạy ở **30 Hz**, stateless giữa các tick (mọi trạng thái nằm trong
`BotContext` rõ ràng, serializable — để debug/replay được).

## 2. Kết hợp hai mô hình: State Machine + Behavior Tree

```mermaid
flowchart TB
    SM["Top-level FSM (mode của bot)"] -->|in_combat| BT1["BT: Combat"]
    SM -->|navigating| BT2["BT: Navigate & Loot"]
    SM -->|ui_menu| BT3["BT: UI Flow (dialog, respawn, party)"]
    SM -->|dead| BT4["BT: Recovery"]

    subgraph BT1 ["BT: Combat (chạy mỗi tick)"]
        A1["Selector"] --> A2["Dodge/Block nếu boss telegraph"]
        A2 --> A3["Potion nếu HP < ngưỡng"]
        A3 --> A4["Suit Change nếu điều kiện đổi suit"]
        A4 --> A5["CastSkill theo rotation của class"]
        A5 --> A6["Reposition / giữ khoảng cách"]
    end
```

- **FSM quản chế độ lớn** (đang ở menu, đang đánh, đang chết...) — tránh behavior tree phình to.
- **BT quản quyết định trong combat** — dễ thêm/bớt rule theo class/profile, priority mặc nhiên bởi thứ tự Selector.

## 3. Perception fusion

```cpp
struct Perception {
    std::shared_ptr<const GameState> memory;  // từ MemoryReader (ưu tiên)
    std::shared_ptr<const ScreenFacts> vision; // từ VisionModule (bổ sung/khi memory fail)
};
```

Quy tắc: field nào memory cung cấp được thì tin memory; field thiếu (ví dụ dialog box)
dùng vision bù. DecisionEngine chỉ thấy `Perception` hợp nhất, không biết nguồn.

## 4. Các quyết định cốt lõi

### 4.1 Dodge/Block (giá trị "pro" cao nhất)
- Đầu vào: `boss.state_id` trong telegraph table (xem [05-game-knowledge.md](05-game-knowledge.md)).
- Logic: khi thấy telegraph có windup W, tính `reaction_delay + input_latency < W`? Nếu đủ → lệnh dodge tại thời điểm tối ưu (không né sớm gây hụt i-frame, không né muộn).
- Không đủ thời gian → chọn reduce damage (block với Warlord) hoặc chấp nhận ăn đòn nhưng giữ DPS.

### 4.2 Rotation engine
- Rotation là **danh sách skill có điều kiện**, không phải script cứng:
```json
{ "priority_skills": [
    { "skill": "S1", "if": "cd_ready && boss_staggered" },
    { "skill": "S2", "if": "cd_ready && mp > 30" },
    { "skill": "S3", "if": "always" } ],
  "suit_switch": { "to": "offensive", "if": "burst_ready && boss_hp_pct > 20" } }
```
- Engine chấm điểm từng skill mỗi tick, chọn cái có điểm cao nhất khả thi — thay vì bấm thứ tự cứng, bot phản ứng với tình huống.

### 4.3 Targeting
- Chấm điểm entity: `score = threat + is_elite*2 + (1 - hp_pct) − distance*k`.
- Luôn ưu tiên: add hồi máu/summoner > elite > boss (cấu hình theo profile).

### 4.4 Navigation
- Waypoint-based cho field/dungeon; obstacle handling: nếu không tiến sau 2s → đổi hướng ngẫu nhiên nhỏ (giống người test path), không cần pathfinding A* đủ bản đồ ở giai đoạn đầu.

## 5. An toàn & thất bại

- Mỗi BT node có **timeout riêng** — node treo quá 5s ⇒ FSM về `Navigating` để thoát kẹt.
- **Blacklist hành vi:** hành động nào lặp vô hiệu ≥ 3 lần (bấm skill không vào) sẽ bị cấm 30s và báo telemetry.
- Chế độ `--dry-run`: DecisionEngine chạy full nhưng mọi ActionCommand chỉ được log — dùng để soát logic không rủi ro.
- **Replay/debug:** mỗi tick ghi `GameState + ActionCommand` ra ring buffer 60s; khi có sự cố, dump 60s trước sự cố ra file JSON để tái hiện.

## 6. KPI của module (liên kết [00-overview.md](00-overview.md))

- DPS uptime ≥ 85% (đo từ telemetry: % thời gian đang trong cast/skill anim).
- Tỷ lệ dodge thành công ≥ 70% telegraphed attacks.
- Số lần "kẹt" (>30s không thay đổi trạng thái) = 0 trên 1 phiên 4h.
