diff --git a/scripts/domain/attribute_formula.gd b/scripts/domain/attribute_formula.gd new file mode 100644 index 0000000..345917a --- /dev/null +++ b/scripts/domain/attribute_formula.gd @@ -0,0 +1,54 @@ +## AttributeFormula — 属性合成公式的唯一实现处 +## 纯静态函数:无状态、不依赖 PlayerStats / EventBus / 场景树,故可脱离游戏进程单元测试 +## 权威来源:docs_dev/specs/2026-07-31-player-attributes-design.md §2.1 / §2.3 +class_name AttributeFormula +extends RefCounted + +enum Combine { HYBRID, INVERSE, ADD_INT } + +const _COMBINE_NAMES: Dictionary = { + "hybrid": Combine.HYBRID, + "inverse": Combine.INVERSE, + "add_int": Combine.ADD_INT, +} + +## JSON 的 combine 字符串 → 枚举;未知值 push_error 并回退 HYBRID +static func combine_from_string(s: String) -> Combine: + if _COMBINE_NAMES.has(s): + return _COMBINE_NAMES[s] + push_error("AttributeFormula: 未知 combine「%s」,回退 hybrid" % s) + return Combine.HYBRID + +## 唯一的公式入口 +## base —— attributes.json 的基准值 +## mods —— [{"mode": "flat"|"pct", "value": float}, ...];调用方负责只传本属性的加成 +## attr_def —— attributes.json 中该属性的定义(读 combine / hard) +## 返回统一为 float;add_int 属性由调用方做 int() 转换(公式模块不感知目标字段类型) +static func compute(base: float, mods: Array, attr_def: Dictionary) -> float: + var combine: Combine = combine_from_string(String(attr_def.get("combine", "hybrid"))) + var hard: float = float(attr_def.get("hard", 0.0)) + var flat_sum: float = 0.0 + var pct_prod: float = 1.0 + for m in mods: + var mode: String = String(m.get("mode", "flat")) + var v: float = float(m.get("value", 0.0)) + if mode == "flat": + flat_sum += v + elif mode == "pct": + if combine == Combine.ADD_INT: + push_error("AttributeFormula: add_int 属性不接受 pct 加成(value=%f),已忽略" % v) + continue + # 连乘而非线性求和:三条 +20% = ×1.728 而非 ×1.6,避免后期线性失控 + pct_prod *= (1.0 + v) if combine == Combine.HYBRID else (1.0 - v) + else: + push_error("AttributeFormula: 未知 mode「%s」,已忽略" % mode) + match combine: + Combine.ADD_INT: + var ri: float = floorf(base + flat_sum) + return clampf(ri, 0.0, hard) if hard > 0.0 else maxf(0.0, ri) + Combine.INVERSE: + # 越低越快:hard 是**下限** + return maxf(hard, (base + flat_sum) * pct_prod) + _: + var r: float = maxf(0.0, (base + flat_sum) * pct_prod) + return minf(r, hard) if hard > 0.0 else r # hard=0.0 约定为「不钳制」 diff --git a/scripts/domain/attribute_formula.gd.uid b/scripts/domain/attribute_formula.gd.uid new file mode 100644 index 0000000..453e7a0 --- /dev/null +++ b/scripts/domain/attribute_formula.gd.uid @@ -0,0 +1 @@ +uid://c41seas4fosx2