feat(shop): 货架 C 状态与购买——shop 段驱动可售集合,唯一写入点重建加成
可售集合纯数据驱动:attributes.json 的 shop 段缺失即不可售,将来解除 7 个 被阻塞属性的前置后只需加一段 JSON,零代码。 PlayerStats 新增通用生效值访问器 get_attr_value(attr_id),消除商店/UI 侧 本应出现的三处硬编码 match attr_id;_attr_effective 与裸字段在同一处 (_recompute_attrs)更新,避免两者不同步导致显示值与生效值脱节。 _attr_purchases 只存次数;每属性至多一条 _modifiers、value 为合并值 (pct 下 n 次 +step 等价于单条 (1+step)^n − 1)。这样出售退款只需次数减一后 重算,不必给 PlayerStats 新增按条撤销的 API。 所有改动次数的路径都收在 _apply_attr_purchases()——两份状态不同步会产生 「显示买了 3 次但加成只有 2 次」且无任何诊断,收敛写入点是唯一防线。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ var _invuln_until_msec: int = 0 # < now 表示可受击;受击后设为 now
|
||||
# 属性定义与加成来源(非热路径)
|
||||
var _attr_def: Dictionary = {} # attributes.json 全量定义,只读
|
||||
var _modifiers: Array[Dictionary] = [] # [{attr_id, mode, value, source}]
|
||||
var _attr_effective: Dictionary = {} # attr_id → 生效值;供冷路径(商店/UI/设计器)通用读取
|
||||
|
||||
# ── 魔力(MVP)─────────────────────────────────────────
|
||||
var mana: float = 100.0
|
||||
@@ -233,6 +234,14 @@ func _recompute_attrs() -> void:
|
||||
cast_delay_mod = _compute_attr("cast_delay_mod")
|
||||
hp_max = _compute_attr("hp_max")
|
||||
hp = minf(hp, hp_max) # hp_max 下调(出售退款/换杖)时避免 hp > hp_max
|
||||
# 冷路径通用视图:热路径(move_speed 每物理帧)仍走裸字段,此表只服务商店/UI 等按 id 取值的场景。
|
||||
# 必须与裸字段在同一处更新——两者不同步会让商店显示的值与实际生效值不一致且无诊断。
|
||||
_attr_effective = {
|
||||
"cpu_limit": float(cpu_limit),
|
||||
"move_speed": move_speed,
|
||||
"cast_delay_mod": cast_delay_mod,
|
||||
"hp_max": hp_max,
|
||||
}
|
||||
stats_changed.emit()
|
||||
|
||||
func _compute_attr(attr_id: String) -> float:
|
||||
@@ -247,3 +256,10 @@ func _compute_attr(attr_id: String) -> float:
|
||||
assert(false, "PlayerStats: 缺少属性「%s」——生效值将为 0,运行时 push_error 不可见" % attr_id)
|
||||
return 0.0
|
||||
return AttributeFormula.compute(float(d.get("base", 0.0)), _mods_for(attr_id), d)
|
||||
|
||||
## 按 id 取生效值(冷路径)。热路径请直接读裸字段(move_speed 等),本函数有字典查找开销。
|
||||
func get_attr_value(attr_id: String) -> float:
|
||||
if not _attr_effective.has(attr_id):
|
||||
push_error("PlayerStats: 未知属性「%s」,无生效值" % attr_id)
|
||||
return 0.0
|
||||
return float(_attr_effective[attr_id])
|
||||
|
||||
Reference in New Issue
Block a user