3.8 KiB
3.8 KiB
ADR-001: 普通骰子和 Lucky Dice 特殊结果分成两套集合
Status
Accepted
Date
2026-06-22
Context
FishDice 第一版需要同时支持普通骰子主循环和 Lucky Dice 特殊分支。普通 Roll 的骰子面是 2 / 3 / 4 / 5 / 6 / clover,它们的职责是参与组合事实、规则匹配、普通奖励、Clover bonus 和触发 Lucky Dice。
Lucky Dice 进入后使用的是特殊结果,例如 rocket、thief。这些结果的职责不是点数结算,而是决定后续进入哪个短玩法,以及进入该玩法的哪个模式。
如果两类结果混用同一套裸字符串或同一个骰子集合,会带来几个风险:
- 普通组合匹配器可能错误处理 Lucky Dice 特殊结果。
- 策划配置容易把数字奖励结果和玩法入口结果混在一起。
- 日志中很难判断某个 key 是普通骰子面还是 Lucky Dice 入口。
- 后续扩展更多特殊结果时,会污染普通 Roll 的规则空间。
Decision
普通骰子和 Lucky Dice 特殊结果使用两套独立集合,并在数据模型中保留集合类型。
建议在代码中使用等价于以下概念的结构:
public enum DiceSetType
{
Normal,
Lucky
}
public readonly struct DiceFace
{
public DiceSetType SetType { get; }
public string Key { get; }
public int? NumberValue { get; }
}
普通骰子集合只包含主循环 Roll 面。Lucky Dice 集合只包含特殊入口结果。普通组合事实构建器只接受 DiceSetType.Normal 的 Roll 结果,遇到 Lucky Dice 特殊结果时必须拒绝或返回结构化错误。
Alternatives Considered
使用同一个 DiceFace 字符串集合
优点:
- 第一版实现最省事。
- 配置表字段少。
缺点:
clover、rocket、thief、数字面都会落在同一个匹配空间。- 普通 matcher 需要不断写排除逻辑,防止特殊结果进入普通奖励结算。
- 日志和调试工具无法从类型上识别语义边界。
结论:拒绝。它会把第一版的简单实现成本转化成后续规则扩展和排查成本。
使用同一个集合,但通过 key 前缀区分
示例:normal_2、normal_clover、lucky_rocket。
优点:
- 比裸字符串更可读。
- 不需要额外枚举字段。
缺点:
- 类型边界依赖命名约定,配置错误时很难在编译或校验阶段发现。
- matcher 仍然会看到同一批 key,需要人工遵守前缀规则。
- 后续如果出现多套普通骰子或多套特殊结果,前缀会越来越复杂。
结论:拒绝。前缀可以作为日志展示或配置命名辅助,但不能替代结构化集合类型。
使用两套独立数据模型
普通骰子面和 Lucky Dice 特殊结果完全使用不同 class。
优点:
- 类型隔离最强。
- 不容易误用。
缺点:
- Roll、展示槽、日志等通用结构需要重复建模。
- 第一版通用流程会变得偏重。
结论:暂不采用。当前使用共同的 DiceFace 概念加 DiceSetType 已能满足隔离需求。
Consequences
正向影响:
- 普通 Roll 和 Lucky Dice 的语义边界清晰。
- 普通组合 matcher 不需要理解特殊玩法入口。
- 日志可以明确记录本次处理的是
Normal还是Lucky集合。 - 后续新增特殊结果不会污染普通奖励规则。
代价和约束:
- 配置工具和调试界面必须展示集合类型。
- 配置加载需要校验普通集合不能包含 Lucky Dice 特殊结果。
- 代码中不能到处裸写 key,需要集中定义 key 或配置入口。