Files
gplan-2/game/core/status_container.gd
T

134 lines
5.3 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
# Validation Question: 玩家能否无指导在一场完整战斗中经历「开战→挖掘规划→轮末结算逐行兑现→胜/负」并逐行归因(P2)
# Date: 2026-09-10
# 增益状态·统一状态容器:三向效果同构实例 · 单一时限量纲 remaining_settlements(先兑付后衰减)·
# 按行叠层(stack 授予不触碰 R)· 授予时数值快照抗热重载 · 零随机零信号。
# 注:MVP 单敌假设经 敌人系统 回验不成立(定多敌)——enemy 侧 side-wide 语义按 registry 改写执行。
# 实现 GDD:design/archive/gdd-2026-09-14/b3-status-effect.md(系统 ID B3·增益状态——registry 追溯锚,命名规范见 CLAUDE.md)
class_name StatusContainer
extends RefCounted
var db: ConfigDb
var _open := false
var _instances: Array[Dictionary] = [] # 插入序;元素 = {effect_id, layers, remaining, source_class, origin_cell, round_index, snap_m, snap_unit, snap_display}
func _init(db_ref: ConfigDb) -> void:
db = db_ref
func notify_battle_start() -> bool:
if _open:
push_warning("[增益状态] Active 下重复 notify_battle_start(EC11)")
return false
_open = true
_instances.clear()
return true
# 唯一授予入口(挖掘期零直写——战斗结算 结算编排期统一调用)。
# duration_override(敌人技能池批——增益技施放):技能行 buff_duration 覆盖状态行时长(-1 = 状态表值)。
# 施放是持续量的 owner(proposal §2.2 字段词汇表"buff_duration 增益持续回合数");其他授予路径传缺省。
func apply_effect(effect_id: String, source: Dictionary, duration_override: int = -1) -> bool:
if not _open:
push_warning("[增益状态] 域闭授予(EC1)")
return false
var row: Dictionary = db.status_effect_row(effect_id)
if row.is_empty():
push_warning("[增益状态] 未知 effect_id(EC2): %s" % effect_id)
return false
var policy: String = row.stack_policy
var dur: int = int(row.duration_settlements) if duration_override < 1 else duration_override
var max_stack: int = int(row.max_stack)
# 授予时快照(抗表重载——EC9)
var snap_m: int = int(row.magnitude)
if source.has("magnitude_override"):
snap_m = int(source["magnitude_override"]) # 旗溢价贯穿(迭代14② T4——标中旗 percent 形态同乘快照;缺省路径零扰动)
var snap_unit: String = String(row.unit)
var snap_display: String = String(row.display_name)
match policy:
"independent":
_instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display))
return true
"refresh":
for inst in _instances:
if inst.effect_id == effect_id:
inst.remaining = dur # R ← D(取实例快照 D——同表值);L 不变;空刷新恒 true
return true
_instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display))
return true
"stack":
for inst in _instances:
if inst.effect_id == effect_id:
if inst.layers >= max_stack:
return false # 满层拒绝原子——容器零写入(拒收由战斗结算记"奖励未生效"行)
inst.layers += 1 # stack 授予不触碰 R(spec 决议)
return true
_instances.append(_new_instance(effect_id, 1, dur, source, snap_m, snap_unit, snap_display))
return true
push_warning("[增益状态] 未知 stack_policy")
return false
func _new_instance(effect_id: String, layers: int, dur: int, source: Dictionary, m: int, unit: String, display: String) -> Dictionary:
return {
"effect_id": effect_id,
"layers": layers,
"remaining": dur,
"source_class": String(source.get("source_class", "")),
"origin_cell": source.get("origin_cell", Vector2i(-1, -1)),
"round_index": int(source.get("round_index", 0)),
"snap_m": m,
"snap_unit": unit,
"snap_display": display,
}
# 只读投影(插入序稳定枚举;六归因字段 + display_name + 授予时快照)
func query_active(side: String) -> Array:
var out: Array[Dictionary] = []
for inst in _instances:
var row: Dictionary = db.status_effect_row(inst.effect_id)
if row.is_empty():
continue
if String(row.target_side) != side:
continue
out.append({
"effect_id": inst.effect_id,
"layers": inst.layers,
"remaining_settlements": inst.remaining,
"source_class": inst.source_class,
"origin_cell": inst.origin_cell,
"round_index": inst.round_index,
"display_name": inst.snap_display,
"snap_magnitude": inst.snap_m,
"snap_unit": inst.snap_unit,
"effective_value": inst.layers * inst.snap_m, # L×m 加算(percent 越域不 clamp——收口归 战斗结算)
})
return out
# 轮末衰减:先兑付后衰减;每实例恰 −1;归 0 移除;返回移除数 k(恰一次执行由 战斗结算 编排信任承载)
func decay_round() -> int:
if not _open:
return 0 # EC13 域闭兜底读法——容器恒空,无独立分支不诊断
var k := 0
var survivors: Array[Dictionary] = []
for inst in _instances:
inst.remaining -= 1
if inst.remaining <= 0:
k += 1
else:
survivors.append(inst)
_instances = survivors
return k
func notify_battle_end() -> bool:
if not _open:
push_warning("[增益状态] BattleClosed 下重复 notify_battle_end(EC11)")
return false
_open = false
_instances.clear() # 全清不跨战斗
return true