From c6e89a0787c8c511ff49c21eef6447fc321a97b7 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 3 Aug 2026 10:55:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(shop):=20PriceFormula=20=E5=AE=9A=E4=BB=B7?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E2=80=94=E2=80=94geometric=20/=20linear=20/?= =?UTF-8?q?=20flat=20=E4=B8=89=E6=9B=B2=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 定价集中于单一纯静态模块,无状态零依赖,故可脱离游戏进程单元断言 (与 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 --- scripts/domain/price_formula.gd | 45 +++++++++++++++++++++++++++++ scripts/domain/price_formula.gd.uid | 1 + 2 files changed, 46 insertions(+) create mode 100644 scripts/domain/price_formula.gd create mode 100644 scripts/domain/price_formula.gd.uid diff --git a/scripts/domain/price_formula.gd b/scripts/domain/price_formula.gd new file mode 100644 index 0000000..a7622a5 --- /dev/null +++ b/scripts/domain/price_formula.gd @@ -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) diff --git a/scripts/domain/price_formula.gd.uid b/scripts/domain/price_formula.gd.uid new file mode 100644 index 0000000..3fc0196 --- /dev/null +++ b/scripts/domain/price_formula.gd.uid @@ -0,0 +1 @@ +uid://b4h1n0puafawv