187 lines
7.6 KiB
GDScript
187 lines
7.6 KiB
GDScript
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||
## 格面资源身份层(受理账面):授予期零随机、Ready 后只读无重掷(垂直切片验证:格面资源同种子逐位回放)。
|
||
## 摇号面已分家(architecture v1.29 轴戊):装配期格面资源摇号(池行装配/区间行 roll/权重合成)
|
||
## 归 RewardRoller(本件 _init 自建自持有,外部经 roller 组件出口直达);本类保留受理账面
|
||
## (grant/身份查询/快照/retire/polarity_row)与 roll_board 装配序(守卫→复位→委托→收纳→ready)。
|
||
## 衔接面=格→奖励身份映射批束(键集封闭 mapping/power_roll/clear_identity/clear_power——掷格件产出、
|
||
## 本件接收收纳)。
|
||
## roll_board 每板恰一次(Ready 相位再调 = double-roll 拒绝;换图 = 合法新批);禁引入授予期随机或重掷。
|
||
## 极性路由归战斗结算授予段——本类只提供 polarity_row 的(类型, 极性)→ 字典行查询面。
|
||
class_name RewardEconomy
|
||
extends RefCounted
|
||
|
||
var db: ConfigDb
|
||
# 违约中止通道——缺省空 Callable 退化 push_error+assert(现行为逐字保持);组装层注入
|
||
# orchestrator._fatal,使 release 剥 assert 后仍经 battle_result 封印真中止。
|
||
# 属性 setter 同拍转注 roller(构造序:economy._init 不触该成员、编排器注入晚于 _init——
|
||
# roller 已在座时赋值即转注,掷格件违约封印与受理面同一通道)
|
||
var fatal_handler: Callable = Callable():
|
||
set(v):
|
||
fatal_handler = v
|
||
if roller != null:
|
||
roller.fatal_cb = v
|
||
var _phase := "unrolled" # unrolled → ready → retired
|
||
var _mapping := {} # Vector2i → reward_id(正向行身份——格面资源类型由 cell_type 承载)
|
||
var _power_roll := {} # Vector2i → 区间行 roll 值(身份批内追加取号)
|
||
var _clear_power := -1 # 清图槽区间行 roll 值(-1 = 定值行)
|
||
var _granted := {} # Vector2i → 快照(重复受理幂等返回)
|
||
var _clear_identity := ""
|
||
var _clear_granted := false
|
||
var _clear_snapshot := {}
|
||
var roller: RewardRoller # 掷格件(摇号面单点——自建自接线,BoardInstaller.generator 先例)
|
||
|
||
|
||
func _init(db_ref: ConfigDb, rng_ref: RngServiceCore) -> void:
|
||
db = db_ref
|
||
roller = RewardRoller.new({"db": db_ref, "rng": rng_ref, "fatal_cb": fatal_handler})
|
||
|
||
|
||
# ---- install 期 roll(恰一次;Ready 相位再调拒绝)----
|
||
|
||
# eligible = 全部非封锁格(安全格 + 危险格,战斗结算 install 编排供给;封锁格不入清单)。
|
||
# 装配序:相位守卫(E5,先行——违约路径零取号)→ 空清单 fatal(E1)→ 受理面复位五拍 →
|
||
# 委托掷格件摇号 → 收纳批束 → ready
|
||
func roll_board(eligible: Array) -> bool:
|
||
# 相位守卫:Ready 再调 = double-roll 拒绝(E5);Retired(换图后)= 合法新批重置(E6 roll 半)
|
||
if _phase == "ready":
|
||
push_warning("[地图战斗资源] roll_board 相位守卫拒绝(E5——double-roll 编排违约显式暴露)")
|
||
return false
|
||
if eligible.is_empty():
|
||
_fatal("roll_board 空资格清单(E1 兜底 fail-fast——排雷玩法 不变式④前置)")
|
||
return false
|
||
_granted.clear() # 新批重置映射受理面(旧图快照消亡)
|
||
_power_roll.clear()
|
||
_clear_power = -1
|
||
_clear_granted = false
|
||
_clear_snapshot = {}
|
||
var batch := roller.roll(eligible)
|
||
_mapping = batch.mapping
|
||
_power_roll = batch.power_roll
|
||
_clear_identity = batch.clear_identity
|
||
_clear_power = batch.clear_power
|
||
_phase = "ready"
|
||
return true
|
||
|
||
|
||
# ---- 查询面(Ready 只读;未开格恒可查——秘宝 桥接结构前提)----
|
||
|
||
# 相位只读出口(诊断观测面——unrolled/ready/retired;消费方禁直读 _phase 下划线字段)
|
||
func phase() -> String:
|
||
return _phase
|
||
|
||
|
||
# 静默映射查询(批量工具用——零 E19 诊断;语义 = query_identity 非 null 的布尔投影)
|
||
func is_mapped(cell: Vector2i) -> bool:
|
||
return _phase == "ready" and _mapping.has(cell)
|
||
|
||
|
||
func query_identity(cell: Vector2i) -> Variant:
|
||
if _phase != "ready":
|
||
_diag("query_identity 非 Ready 相位")
|
||
return null
|
||
var rid: String = _mapping.get(cell, "")
|
||
if rid == "":
|
||
_diag("query_identity 清单外坐标(E19——未分配非错误态;封锁格不入清单)")
|
||
return null
|
||
var row: Dictionary[Variant, Variant] = _row_by_id(rid)
|
||
return {"reward_id": rid, "cell_type": String(row.cell_type), "tier": int(row.tier)}
|
||
|
||
|
||
# 身份完整快照(轮内刷新批次隔离用——facts 缓冲期捕获,授予期 mapping 可能已换批):
|
||
# 返回与 grant 相同结构的快照(cell_type/display_name/power/effect_id/stacks 齐备),
|
||
# 供 战斗结算 冻结授予直通
|
||
func query_full_snapshot(cell: Vector2i) -> Variant:
|
||
if _phase != "ready":
|
||
_diag("query_full_snapshot 非 Ready 相位")
|
||
return null
|
||
var rid: String = _mapping.get(cell, "")
|
||
if rid == "":
|
||
return null
|
||
return _snapshot(_row_by_id(rid), int(_power_roll.get(cell, -1)))
|
||
|
||
|
||
# ---- 受理面(战斗结算 编排期调用;E6 收口:失败统一返回 null)----
|
||
|
||
func grant(cell: Vector2i) -> Variant:
|
||
if _phase != "ready":
|
||
_diag("grant 非 Ready 相位(E4/E7)")
|
||
return null
|
||
if not _mapping.has(cell):
|
||
_diag("grant 清单外坐标(E19)")
|
||
return null
|
||
if _granted.has(cell):
|
||
_diag("grant 同格重复受理(E18 幂等兜底——受理恰一次归 战斗结算)")
|
||
return _granted[cell]
|
||
var snap := _snapshot(_row_by_id(_mapping[cell]), int(_power_roll.get(cell, -1)))
|
||
_granted[cell] = snap
|
||
return snap
|
||
|
||
|
||
func grant_clear() -> Variant:
|
||
if _phase != "ready":
|
||
_diag("grant_clear 非 Ready 相位")
|
||
return null
|
||
if _clear_granted:
|
||
_diag("grant_clear 同批次重调(E18 幂等兜底)")
|
||
return _clear_snapshot
|
||
var row: Dictionary[Variant, Variant] = _row_by_id(_clear_identity)
|
||
_clear_snapshot = _snapshot(row, _clear_power)
|
||
_clear_granted = true
|
||
return _clear_snapshot
|
||
|
||
|
||
func retire_mapping() -> bool:
|
||
if _phase != "ready":
|
||
_diag("retire_mapping 非 Ready 相位(幂等受理族)")
|
||
return false
|
||
_phase = "retired"
|
||
_mapping.clear()
|
||
_power_roll.clear()
|
||
_clear_power = -1
|
||
_clear_identity = ""
|
||
return true
|
||
|
||
|
||
# ---- 极性路由字典面(战斗结算 授予段消费):(类型, 极性)→ 格面池字典行(拷贝出口)----
|
||
|
||
func polarity_row(cell_type: String, polarity: String) -> Dictionary:
|
||
for row in db.run.reward.rows:
|
||
if String(row.get("pool_tag", "")) == "cell" \
|
||
and String(row.get("cell_type", "")) == cell_type \
|
||
and String(row.get("polarity", "")) == polarity:
|
||
return row.duplicate() # E6:表行值拷贝出口——消费方禁改配置源
|
||
_fatal("极性字典缺行(八型×正负齐备为加载前提): %s %s" % [cell_type, polarity])
|
||
return {}
|
||
|
||
|
||
func _row_by_id(rid: String) -> Dictionary:
|
||
for row in db.run.reward.rows:
|
||
if row.reward_id == rid:
|
||
return row
|
||
return {}
|
||
|
||
|
||
func _snapshot(row: Dictionary, rolled_power: int = -1) -> Dictionary:
|
||
var snap := {
|
||
"reward_id": String(row.reward_id),
|
||
"cell_type": String(row.cell_type),
|
||
"display_name": String(row.display_name),
|
||
"power": int(row.power) if rolled_power < 0 else rolled_power, # 区间行 = 身份批 roll 值(E14 授予时快照口径)
|
||
"tier": int(row.tier),
|
||
"stacks": int(row.get("stacks", 1)),
|
||
}
|
||
if String(row.get("effect_id", "")) != "":
|
||
snap["effect_id"] = String(row.effect_id)
|
||
return snap
|
||
|
||
|
||
func _diag(msg: String) -> void:
|
||
push_warning("[地图战斗资源] " + msg)
|
||
|
||
|
||
func _fatal(msg: String) -> void:
|
||
push_error("[地图战斗资源/fatal] " + msg)
|
||
if fatal_handler.is_valid():
|
||
fatal_handler.call(msg) # 组装层注入 orchestrator._fatal(封印传导,不绕第二中止通道)
|
||
assert(false, msg)
|