# Lucky Dice 核心流程架构蓝图

## 文档信息

- 来源 PRD：[lucky-dice-core-flow-prd.md](../PRD/lucky-dice-core-flow-prd.md)
- 关联需求：[lucky-dice-core-flow-requirements.md](../Requirements/lucky-dice-core-flow-requirements.md)
- 关联规格：[lucky-dice-core-flow-spec.md](../Specs/lucky-dice-core-flow-spec.md)
- 适用项目：FishDice Unity 项目
- Unity 版本：2022.3.62f2c1
- 生成日期：2026-06-22

本文档定义 Lucky Dice 第一版核心闭环的架构蓝图。它不替代需求或接口规格，而是回答后续实现时最容易失控的几个问题：

- 普通 Roll、组合规则、Lucky Dice、目标玩法入口之间怎么分层。
- 哪些模块可以通过配置扩展，哪些模块才需要新增代码。
- 数据从玩家 Roll 到最终玩法模式如何流动。
- 追踪、兜底、测试和 Unity 落地目录应如何组织。

当前项目尚未落地生产 C# 玩法模块，因此本文档描述的是第一版建议架构，而不是对现有代码的复盘。

## 架构定位

Lucky Dice 不是普通骰子的二次 Roll，也不是一个独立短玩法本体。它在架构中承担的是“特殊玩法入口选择器”职责：

```text
普通 Roll 负责产出组合事实
组合规则负责决定业务行为
Lucky Dice 负责从特殊结果池中选择目标入口
目标玩法模块负责执行 Slap Down / Treasure Heist 等具体内容
```

第一版架构应优先保证三件事：

1. **可扩展**：新增组合、骰子数量、特殊结果、目标玩法模式时优先改配置。
2. **可测试**：组合事实、规则匹配、筛选、权重随机都能用确定性输入测试。
3. **可追踪**：一次 Roll 的所有关键决策能用同一个 `RollSessionId` 串起来。

## 架构原则

### 两套骰子集合隔离

普通骰子集合只表达主循环 Roll 的结果，例如 `2 / 3 / 4 / 5 / 6 / clover`。Lucky Dice 特殊结果集合只表达后续玩法入口，例如 `rocket / thief`。

两者不能混用同一套裸字符串，也不能让普通组合匹配器直接处理 Lucky Dice 特殊结果。

### 规则匹配先于行为执行

普通 Roll 结果必须先转换成结构化 `ComboFacts`，再交给规则匹配器。业务行为不应读取“左骰 / 右骰”这类固定位置状态。

```text
DiceRollResult
→ ComboFacts
→ ComboRule
→ RollActionSpec
→ IRollActionExecutor
```

### 配置增长优先于代码增长

组合数量增加时，应增加 `ComboRule` 配置。只有出现新的通用匹配能力时才新增 matcher，只有出现新的通用业务行为时才新增 executor。

禁止形成下面这种随组合数量膨胀的结构：

```text
clover_clover -> TriggerLuckyDice()
2_clover      -> GrantCloverReward()
3_clover      -> GrantCloverReward3()
...
```

### 流程服务不依赖表现层

核心流程可以产出展示所需数据，例如 Lucky Dice 标题、倍率、结果槽和目标玩法反馈，但不能依赖 UI 动画完成时序来做核心决策。

第一版表现层只消费核心结果，不参与筛选、随机和模式解析。

## 总览图

```mermaid
flowchart TD
    Player["玩家触发 Roll"] --> RollService["DiceRollService"]
    RollService --> FactBuilder["ComboFactBuilder"]
    FactBuilder --> RuleMatcher["ComboRuleMatcher"]
    RuleMatcher --> ActionDispatcher["RollActionDispatcher"]

    ActionDispatcher --> Reward["RewardExecutor"]
    ActionDispatcher --> Multiplier["MultiplierExecutor"]
    ActionDispatcher --> LuckyTrigger["TriggerLuckyDiceExecutor"]

    LuckyTrigger --> LuckyFlow["LuckyDiceFlowService"]
    LuckyFlow --> CandidateFilters["CandidateFilters"]
    CandidateFilters --> WeightedPicker["WeightedPicker"]
    WeightedPicker --> SlotBuilder["ResultSlotBuilder"]
    SlotBuilder --> ModeResolver["TargetModeResolver"]
    ModeResolver --> Launcher["TargetModeLauncher"]
    Launcher --> TargetGameplay["Slap Down / Treasure Heist"]
    TargetGameplay --> MainLoop["回到主循环"]

    RollService -.-> Trace["RollTrace"]
    RuleMatcher -.-> Trace
    ActionDispatcher -.-> Trace
    LuckyFlow -.-> Trace
    ModeResolver -.-> Trace
```

## 分层架构

| 层级 | 主要职责 | 允许依赖 | 禁止依赖 |
| --- | --- | --- | --- |
| 配置层 | 提供骰子集合、规则、候选结果、目标模式映射 | Unity 资源、本地 JSON、静态配置 | UI 动画状态、运行时随机结果 |
| Roll 层 | 根据 DiceSet 生成普通骰子结果 | 配置层、随机源 | 规则、奖励、玩法入口 |
| 组合事实层 | 把普通 Roll 结果转为统计事实 | Roll 层数据 | Lucky Dice 特殊结果、UI |
| 规则匹配层 | 按 matcher 和优先级命中规则 | 组合事实、规则配置 | 行为执行器、目标玩法 |
| 行为执行层 | 按 action type 分发通用行为 | 规则结果、上下文服务 | 组合 key 到独立方法映射 |
| Lucky Dice 层 | 候选池、筛选、权重随机、结果槽 | 候选配置、上下文、随机源 | 普通组合 matcher |
| 模式解析层 | `ResultKey -> TargetKey -> ModeKey` | Lucky Dice 结果、上下文、模式配置 | 短玩法内部实现 |
| 表现层 | 展示 Lucky Dice 标题、倍率、结果槽、跳转反馈 | 核心流程产物 | 核心规则决策 |
| 目标玩法层 | 执行 Slap Down / Treasure Heist 等玩法 | TargetModeEntry | Lucky Dice 随机细节 |
| 追踪层 | 汇总关键决策链路 | 所有核心层事件 | 改变玩法结果 |

依赖方向应从上游输入流向下游结果。追踪层可以被各层写入，但不应反向影响流程决策。

## 组件职责

### DiceSetConfigProvider

负责提供骰子集合配置，包括普通骰子面、普通骰子数量、Lucky Dice 特殊结果面、默认结果槽数量。

第一版可以使用 ScriptableObject、JSON 或硬编码本地配置启动，但调用方必须只通过 provider 读取，不把 `2` 颗骰子或 `3` 个结果槽写死到流程逻辑中。

### DiceRollService

负责按骰子集合配置生成 Roll 结果。普通 Roll 会产出多个普通骰子面；Lucky Dice 第一版不需要真的 Roll 三颗特殊骰，而是由候选选择结果再生成展示槽。

该服务只负责“随机出可见骰子面”，不负责奖励、倍率或玩法入口。

### ComboFactBuilder

负责把普通 Roll 结果转成 `ComboFacts`：

- 原始面列表
- 面数量统计
- 数字列表
- 点数和
- Clover 数量
- 无序归一化 key
- 有序 key

它必须支持 N 颗骰子。`2 + clover` 与 `clover + 2` 的无序 key 必须一致。

### ComboRuleMatcher

负责把 `ComboFacts` 与规则配置匹配。matcher 按类型注册，例如 `ExactCombo`、`AllNumbers`、`ContainsFace`、`FaceCountRange`。

第一版按优先级从高到低匹配，默认命中高优先级规则后停止继续匹配，除非规则配置声明可以继续匹配。

### RollActionDispatcher

负责把命中规则中的 `RollActionSpec` 分发给对应 executor。

dispatcher 只能维护 `ActionType -> Executor` 注册表，不能维护组合 key 到方法的映射。

### LuckyDiceFlowService

负责 Lucky Dice 的核心入口选择流程：

```text
构建候选池
→ 筛选候选池
→ 处理空池兜底
→ 权重随机
→ 生成展示结果槽
```

它不直接启动 Slap Down 或 Treasure Heist，而是把选中结果交给模式解析层。

### TargetModeResolver

负责把 Lucky Dice 结果解析到具体目标玩法模式。

第一版映射：

| ResultKey | TargetKey | ModeKey |
| --- | --- | --- |
| `rocket` | `slap_down` | `slap_down_normal` |
| `thief` | `treasure_heist` | `treasure_heist_normal` |

后续教程模式、bonus 模式应通过 mode rule 扩展，而不是在 Lucky Dice 随机代码里写分支。

### TargetModeLauncher

负责统一启动目标玩法。它接收 `TargetModeEntry`，把 Lucky Dice 的结果、倍率、触发来源、`RollSessionId` 传给目标玩法入口。

目标玩法结束后，通过明确的 completion 回调或流程事件回到主循环。

### RollTrace

负责记录一次 Roll 的关键路径。追踪不是埋点管线本身，第一版可以是本地结构化日志或调试面板数据。

最小字段应覆盖：

- 原始普通骰子结果
- 组合事实和归一化 key
- 命中规则和执行行为
- Lucky Dice 初始候选、过滤过程、兜底类型
- 权重随机输入和选中结果
- 解析出的 TargetKey 和 ModeKey
- 最终结果

## 核心数据流

### 普通 Roll

```mermaid
sequenceDiagram
    participant P as Player
    participant R as DiceRollService
    participant F as ComboFactBuilder
    participant M as ComboRuleMatcher
    participant A as RollActionDispatcher
    participant T as RollTrace

    P->>R: Roll(normal_main)
    R->>T: record normal faces
    R->>F: DiceRollResult
    F->>T: record ComboFacts
    F->>M: ComboFacts
    M->>T: record matched rule
    M->>A: RollActionSpec list
    A->>T: record executed actions
```

### Lucky Dice

```mermaid
sequenceDiagram
    participant A as TriggerLuckyDiceExecutor
    participant L as LuckyDiceFlowService
    participant F as CandidateFilters
    participant W as WeightedPicker
    participant S as ResultSlotBuilder
    participant M as TargetModeResolver
    participant T as RollTrace

    A->>L: LuckyDiceContext
    L->>T: record initial candidates
    L->>F: apply filters
    F->>T: record filter decisions
    F->>W: filtered candidates
    W->>T: record random value and selected result
    W->>S: selected candidate
    S->>T: record result slots
    S->>M: selected candidate + context
    M->>T: record target mode
```

## 配置架构

第一版建议至少拆成四类配置：

| 配置 | 示例内容 | 扩展方向 |
| --- | --- | --- |
| DiceSetConfig | `normal_main`、`lucky_dice`、骰子面、骰子数量、默认槽数 | 支持关卡自定义骰子数量 |
| ComboRuleConfig | 规则 id、优先级、matcher、actions | 策划新增组合语义 |
| LuckyDiceCandidateConfig | result、target、weight、enabled、slot count、default | 扩展特殊结果与概率 |
| TargetModeConfig | target、default mode、tutorial mode、bonus mode | 扩展玩法模式 |

配置读取顺序建议：

```text
本地默认配置
→ 关卡或活动覆盖配置
→ 运行时上下文修正
```

远端热更不在第一版范围内，但数据结构应避免与 Unity 场景对象强绑定，给后续迁移到远端配置留出空间。

## Unity 落地目录建议

如果第一版开始实现 C# 模块，建议按运行时核心、配置、表现、测试分开：

```text
FishDice/Assets/FishDice/
  Runtime/
    LuckyDice/
      Core/
        DiceRollService.cs
        ComboFactBuilder.cs
        ComboRuleMatcher.cs
        RollActionDispatcher.cs
        LuckyDiceFlowService.cs
        TargetModeResolver.cs
      Config/
        DiceSetConfig.cs
        ComboRuleConfig.cs
        LuckyDiceCandidateConfig.cs
        TargetModeConfig.cs
      Actions/
        GrantRewardExecutor.cs
        AddMultiplierExecutor.cs
        TriggerLuckyDiceExecutor.cs
        EnterModeExecutor.cs
      Filters/
        EnabledFilter.cs
        ProgressFilter.cs
        SourceFilter.cs
        CooldownFilter.cs
        TutorialFilter.cs
      Presentation/
        LuckyDicePresenter.cs
        LuckyDiceResultSlotView.cs
      Trace/
        RollTrace.cs
        RollTraceLogger.cs
  Tests/
    EditMode/
      LuckyDice/
```

第一版核心流程建议尽量放在纯 C# 类里，减少对 `MonoBehaviour` 的依赖。表现层再通过 Unity 组件消费核心流程产物。

## 错误处理与兜底

### 普通组合未命中

处理策略：

1. 记录 `RollSessionId`、原始骰子面、`NormalizedKey`。
2. 走普通奖励兜底，或在调试模式返回明确错误。
3. 不进入 Lucky Dice。

### Lucky Dice 候选池为空

处理策略：

1. 尝试使用配置的 default result。
2. default result 不可用时，降级为普通奖励兜底。
3. 记录每个 filter 过滤前后的候选数量和空池原因。

### 模式解析失败

处理策略：

1. 记录 `ResultKey`、`TargetKey` 和上下文。
2. 尝试使用目标玩法默认模式。
3. 仍失败时降级为普通奖励兜底，并标记为配置错误。

兜底必须可观测，不能静默吞掉。否则后续配置错误会表现成“玩家只是没进 Lucky Dice”，很难定位。

## 测试架构

测试应围绕外部行为和完整链路，而不是私有实现细节。

### 单元测试

| 测试对象 | 重点 |
| --- | --- |
| ComboFactBuilder | N 骰统计、无序 key、有序 key、Clover 数量 |
| ComboRuleMatcher | 优先级、matcher 类型、停止继续匹配 |
| RollActionDispatcher | 按 action type 分发，不按 combo key 分发 |
| CandidateFilters | enabled、progress、source、cooldown、tutorial 过滤 |
| WeightedPicker | 固定随机源下结果可预测 |
| TargetModeResolver | rocket/thief 映射和默认模式 |

### 链路测试

链路测试是第一版最重要的测试口：

```text
给定 Roll 请求
给定 DiceSet 配置
给定 ComboRule 配置
给定 Lucky Dice 候选池
给定固定随机源
断言最终 outcome
```

最小链路用例：

- `2 + 3` 发普通奖励。
- `2 + clover` 发普通奖励并提供 Clover bonus。
- `clover + clover` 触发 Lucky Dice。
- Lucky Dice 选中 `rocket` 后进入 `slap_down_normal`。
- Lucky Dice 选中 `thief` 后进入 `treasure_heist_normal`。
- 候选池为空时进入可观测兜底。
- 普通骰子数量改成 3 后，组合事实和 matcher 仍可工作。

## 架构决策记录

完整 ADR 存放在 [Docs/Decisions](../Decisions/README.md)。本节只保留摘要，后续决策变更应优先新增或更新独立 ADR 文档。

### ADR-001：普通骰子和 Lucky Dice 特殊结果分成两套集合

[ADR-001](../Decisions/adr-001-separate-normal-and-lucky-dice-sets.md)

上下文：普通骰子负责奖励组合，Lucky Dice 负责玩法入口。两者语义不同。

决策：使用 `DiceSetType.Normal` 和 `DiceSetType.Lucky` 区分集合，不让普通组合匹配器处理 Lucky Dice 特殊结果。

结果：避免数字结算和玩法入口混用。代价是配置和调试界面需要明确展示集合类型。

### ADR-002：Roll 结果先转组合事实，再匹配规则

[ADR-002](../Decisions/adr-002-build-combo-facts-before-rule-matching.md)

上下文：第一版默认 2 颗骰子，但未来需要支持 3 颗、4 颗或关卡自定义骰子数量。

决策：所有普通 Roll 都先生成 `ComboFacts`，matcher 只依赖事实统计，不依赖固定位置。

结果：后续扩骰数量时风险更低。代价是第一版需要多写一层事实构建和测试。

### ADR-003：行为按 action type 注册

[ADR-003](../Decisions/adr-003-dispatch-actions-by-action-type.md)

上下文：如果每个组合 key 绑定一个独立方法，组合增长会让执行层失控。

决策：规则只声明 action 列表，dispatcher 只按 `ActionType` 找 executor。

结果：新增组合多数情况下只改配置。代价是 action 参数需要有清晰校验。

### ADR-004：Lucky Dice 随机源可注入

[ADR-004](../Decisions/adr-004-inject-random-source-for-lucky-dice.md)

上下文：权重随机必须可测试、可回放。

决策：Lucky Dice 权重随机通过 `IRandomSource` 或等价接口注入随机源。

结果：测试可预测，线上问题可复盘。代价是运行时需要统一管理随机源生命周期。

## 新功能开发蓝图

### 新增普通组合

1. 确认现有 matcher 能否表达该组合。
2. 能表达时新增 `ComboRuleConfig`。
3. 需要新业务效果时，优先复用现有 action。
4. 只有确实出现新的通用行为时，新增 executor。
5. 补一条规则匹配测试和一条链路测试。

### 新增 Lucky Dice 特殊结果

1. 在 Lucky Dice 特殊结果集合中新增 `ResultKey`。
2. 在候选配置中声明 `TargetKey`、权重、启用条件、槽数、default 标记。
3. 在目标模式配置中声明默认 `ModeKey`。
4. 补候选筛选测试、权重随机测试和目标模式解析测试。

### 新增目标玩法模式

1. 在 `TargetModeConfig` 中增加模式映射。
2. 如模式选择依赖上下文，新增 mode rule。
3. 不修改 Lucky Dice 随机逻辑。
4. 补 `TargetModeResolver` 测试。

### 接入表现动画

1. 核心流程先产出 `LuckyDiceResultSlots` 和 `TargetModeEntry`。
2. 表现层播放标题、倍率、结果槽和跳转反馈。
3. 动画完成后再调用 launcher，或先启动目标玩法再做过场，取决于产品节奏。
4. 动画失败不能改变核心选择结果。

## 架构治理

实现和评审时重点检查以下规则：

- 核心流程中没有固定读取第 0/1 颗骰子的业务判断。
- 普通骰子和 Lucky Dice 特殊结果没有混用同一套集合。
- 新增组合时没有新增组合 key 到独立方法的 map。
- Lucky Dice 候选必须先筛选再随机。
- 候选池为空、模式解析失败、未命中规则都有可观测兜底。
- `RollSessionId` 贯穿普通 Roll、规则匹配、Lucky Dice、模式解析、目标玩法启动。
- 表现层不参与规则匹配、候选筛选和权重随机。

## 第一版落地顺序

建议按以下顺序实现：

1. 定义核心数据结构和本地默认配置。
2. 实现普通 Roll、组合事实和规则匹配。
3. 实现 action dispatcher 和普通奖励 / Clover bonus / trigger Lucky Dice 执行器。
4. 实现 Lucky Dice 候选筛选、权重随机、结果槽生成。
5. 实现 `rocket -> slap_down_normal` 和 `thief -> treasure_heist_normal` 模式解析。
6. 实现最小表现层：标题、倍率、结果槽、目标玩法跳转反馈。
7. 补齐链路测试和追踪字段。

这个顺序能先把核心闭环跑通，再逐步加表现和更多结果类型，避免一开始被动画和短玩法完整内容拖散。
