Files
gplan-2/game/data/schemas/shop_table.gd
T
DKChan b6629d6848 feat(T2-T6): 五词注释换代——完美排雷→完美封印/板级→整图/门票→专长凭证/正主→现行事实源/落籍→已入库
全局 sed 替换 89 处命中清零(S11 绿)。修复:
- 移交 3 处保留原词(shop.tres:86 display_name、curio_controller:165-166 玩家文案)
- 豁免 enemy.tres:653 历史注记保持"已落籍"
- static_scan.sh 词表追加行与头注恢复禁「」引举句式
- S11 规则文件自身不被自身规则误伤(闸门自指豁免生效)
2026-10-20 20:41:50 +08:00

106 lines
6.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## VERTICAL SLICE - NOT FOR PRODUCTION
## 商店表 schema:GP 掉落/关级加值/商品/共享池货架行族 + 爬价因子的类型化读取(行值 provisional;垂直切片验证:商店房 GP 购买兑现局内成长)。
## 消费面一律走 ConfigDB.shop_* 访问器,禁直读行集。
## 行族不记库存:货架=上限即库存,账本单点在冒险侧;products+加法定价=legacy 面(shop=off 对照),现役走 pool_shelf。
class_name ShopTable
extends Resource
@export var gp_drops: Array[Dictionary] = [] # Array[Dictionary] 键 tier
@export var level_gp_bonus: Array[Dictionary] = [] # Array[Dictionary] 键 level_index
@export var products: Array[Dictionary] = [] # Array[Dictionary] 键 product_id
@export var price_level_bonus: Array[Dictionary] = [] # Array[Dictionary] 键 level_index
@export var pool_shelf: Array[Dictionary] = [] # Array[Dictionary] 键 kind_id
@export var price_a: float = 0.0 # 进店已购次数爬价因子(provisional——0 = 未投值即致命域)
@export var price_b: float = 0.0 # 跨关递增因子(provisional)
const GP_TIERS := ["normal", "elite", "boss"]
const PRODUCT_ATTRS := ["atk", "max_hp", "armor_class"]
const SHELF_KINDS := ["attack", "max_hp", "armor", "ap", "feat_ticket", "heal"] # 货架候选词表(四切边后种 + 专长凭证行 + heal 补回复〔默认原则:分类内成员都入该渠道池〕)
func validate() -> Array:
var errs: Array[Dictionary] = []
# 行族空护栏(legacy 四轴 + pool_shelf 皆为 ConfigDB 消费面——缺族即致命)
for family in ["gp_drops", "level_gp_bonus", "products", "price_level_bonus", "pool_shelf"]:
if get(family).is_empty():
errs.append({"category": "empty_table", "message": "ShopTable 行族为空: %s" % family})
# gp_drops:tier 词表封闭 + 掷定域 + 均值锚 + tier 唯一
var seen_tier := {}
for row in gp_drops:
var tier: String = row.get("tier", "")
if not GP_TIERS.has(tier):
errs.append({"category": "enum", "message": "gp_drops tier 出域 {normal, elite, boss}: %s" % tier})
if seen_tier.has(tier):
errs.append({"category": "dup_key", "message": "gp_drops tier 重复: %s" % tier})
seen_tier[tier] = true
if String(row.get("display_name", "")) == "":
errs.append({"category": "domain", "message": "gp_drops %s display_name 空" % tier})
var rmin: int = int(row.get("roll_min", -1))
var mean: int = int(row.get("mean", -1))
var rmax: int = int(row.get("roll_max", -1))
if rmin < 0 or mean < 0 or rmax < 0 or rmax > 999:
errs.append({"category": "domain", "message": "gp_drops %s 掷定域越域 [0,999]: %d..%d..%d" % [tier, rmin, mean, rmax]})
if rmin > mean or mean > rmax:
errs.append({"category": "cross_field", "message": "gp_drops %s 违反 roll_min(%d) ≤ mean(%d) ≤ roll_max(%d)——均值恒等期望锚" % [tier, rmin, mean, rmax]})
# level_gp_bonus / price_level_bonus:level_index 连续从 1 递增 + 加值域
for family in ["level_gp_bonus", "price_level_bonus"]:
var arr: Array[Variant] = get(family)
var seen := {}
for row in arr:
var li: int = int(row.get("level_index", 0))
if seen.has(li):
errs.append({"category": "dup_key", "message": "%s level_index 重复: %d" % [family, li]})
seen[li] = true
var bonus: int = int(row.get("bonus", -1))
if bonus < 0 or bonus > 999:
errs.append({"category": "domain", "message": "%s level %d bonus 越域 [0,999]: %d" % [family, li, bonus]})
for i in arr.size():
var li: int = int(arr[i].get("level_index", 0))
if li != i + 1:
errs.append({"category": "structural", "message": "%s level_index 不连续(须从 1 连续递增): 位 %d = %d" % [family, i + 1, li]})
break
# products:product_id 唯一 + attr 词表 + 价格域 [1,999]
var seen_pid := {}
for row in products:
var pid: String = row.get("product_id", "")
if pid == "":
errs.append({"category": "domain", "message": "products product_id 空"})
continue
if seen_pid.has(pid):
errs.append({"category": "dup_key", "message": "products product_id 重复: %s" % pid})
seen_pid[pid] = true
if String(row.get("display_name", "")) == "":
errs.append({"category": "domain", "message": "%s display_name 空" % pid})
var attr: String = row.get("attr", "")
if not PRODUCT_ATTRS.has(attr):
errs.append({"category": "enum", "message": "%s attr 出域 {atk, max_hp, armor_class}: %s" % [pid, attr]})
var flat: int = int(row.get("flat_value", 0))
if flat < 1 or flat > 999:
errs.append({"category": "domain", "message": "%s flat_value 越域 [1,999]: %d" % [pid, flat]})
var base: int = int(row.get("base_price", 0))
if base < 1 or base > 999:
errs.append({"category": "domain", "message": "%s base_price 越域 [1,999]: %d" % [pid, base]})
var inc: int = int(row.get("price_increment", -1))
if inc < 0 or inc > 999:
errs.append({"category": "domain", "message": "%s price_increment 越域 [0,999]: %d" % [pid, inc]})
# pool_shelf:kind_id 词表封闭(四切边后种)+ 唯一 + display_name + base_price 域 [1,999]
var seen_kind := {}
for row in pool_shelf:
var kind: String = row.get("kind_id", "")
if not SHELF_KINDS.has(kind):
errs.append({"category": "enum", "message": "pool_shelf kind_id 出域(四切边后种封闭词表): %s" % kind})
if seen_kind.has(kind):
errs.append({"category": "dup_key", "message": "pool_shelf kind_id 重复: %s" % kind})
seen_kind[kind] = true
if String(row.get("display_name", "")) == "":
errs.append({"category": "domain", "message": "pool_shelf %s display_name 空" % kind})
var base: int = int(row.get("base_price", 0))
if base < 1 or base > 999:
errs.append({"category": "domain", "message": "pool_shelf %s base_price 越域 [1,999]: %d" % [kind, base]})
# 价格因子域(乘法语义——A/b 均 ≥1 单调不降;上界防手滑投值;provisional 值本体归难度曲线与Boss)
if price_a < 1.0 or price_a > 3.0:
errs.append({"category": "domain", "message": "price_a 越域 [1.0,3.0]: %f" % price_a})
if price_b < 1.0 or price_b > 2.0:
errs.append({"category": "domain", "message": "price_b 越域 [1.0,2.0]: %f" % price_b})
return errs