feat(shop): PriceFormula 定价模块——geometric / linear / flat 三曲线
定价集中于单一纯静态模块,无状态零依赖,故可脱离游戏进程单元断言
(与 AttributeFormula 同构)。模块不认识「属性/武器/装备」,只认识
{price_base, price_growth, curve},划分点在数据里——故货架 B 与出售退款
可直接复用,不必各写一套。
返回值下限 1:免费购买无意义,且 0 价会让「买不起」的判定失效。
偏离简报字面代码一处:枚举由 `Curve` 改名 `PriceCurve`。Godot 4.7.1
拒绝声明与引擎全局类同名的嵌套枚举("member Curve shadows a native
class",与 class_name 是否注册无关),简报/设计文档中的 `enum Curve`
在本引擎版本下无法编译,属简报代码本身的缺陷而非誊抄误差。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
## PriceFormula — 价格计算的唯一实现处
|
||||
## 纯静态函数:无状态、不依赖 ShopManager / PlayerStats / 场景树,故可脱离游戏进程单元测试
|
||||
## 本模块**不认识**「属性/武器/装备」,只认识 {price_base, price_growth, curve} ——
|
||||
## 划分点在数据里,故货架 B(核心抽取)与子计划 ④(出售退款)可直接复用。
|
||||
## 权威来源:docs_dev/specs/2026-07-31-shelf-c-attribute-shop-design.md §2.1
|
||||
class_name PriceFormula
|
||||
extends RefCounted
|
||||
|
||||
## 命名为 PriceCurve 而非 Curve:Godot 引擎自带全局类 `Curve`(曲线资源),
|
||||
## 嵌套枚举若同名会被解析器拒绝("member Curve shadows a native class"),
|
||||
## 与 class_name 是否注册无关,故不能叫 Curve。此为本任务对简报字面代码的
|
||||
## 唯一必要偏离,详见 task-1-report.md。
|
||||
enum PriceCurve { GEOMETRIC, LINEAR, FLAT }
|
||||
|
||||
const _CURVE_BY_NAME: Dictionary[String, PriceCurve] = {
|
||||
"geometric": PriceCurve.GEOMETRIC,
|
||||
"linear": PriceCurve.LINEAR,
|
||||
"flat": PriceCurve.FLAT,
|
||||
}
|
||||
|
||||
## JSON 的 curve 字符串 → 枚举;未知值 push_error 并回退 GEOMETRIC
|
||||
static func curve_from_string(s: String) -> PriceCurve:
|
||||
if _CURVE_BY_NAME.has(s):
|
||||
return _CURVE_BY_NAME[s]
|
||||
push_error("PriceFormula: 未知 curve「%s」,回退 geometric" % s)
|
||||
return PriceCurve.GEOMETRIC
|
||||
|
||||
## 唯一的价格入口
|
||||
## spec —— 数据文件里的定价段,读 price_base / price_growth / curve
|
||||
## purchased —— 已购次数(0 = 首次购买)
|
||||
## 返回 int(金币是整数),下限 1:免费购买无意义,且 0 价会让「买不起」的判定失效
|
||||
static func compute(spec: Dictionary, purchased: int) -> int:
|
||||
var base: float = float(spec.get("price_base", 0.0))
|
||||
var growth: float = float(spec.get("price_growth", 1.0))
|
||||
var n: int = maxi(purchased, 0)
|
||||
var curve: PriceCurve = curve_from_string(String(spec.get("curve", "geometric")))
|
||||
var raw: float = 0.0
|
||||
match curve:
|
||||
PriceCurve.LINEAR:
|
||||
raw = base + float(n) * growth
|
||||
PriceCurve.FLAT:
|
||||
raw = base
|
||||
_:
|
||||
raw = base * pow(growth, float(n))
|
||||
return maxi(roundi(raw), 1)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b4h1n0puafawv
|
||||
Reference in New Issue
Block a user