## 主角数值账:冒险生命周期主角数值域唯一所有者(生命账与特性持有集、锻造强化快照与调试浮窗 ## 百分比通道、攻击/甲类型轴快照、生效上限/攻击/护甲公式与回血封顶公式单点、续玩生命钳制收口)。 ## 生效公式消费加算日记域汇总(注入 GainJournal 只读引用——无反向引用不成环);GP 钱包归 ## GpWallet、加算日记归 GainJournal——主角账三件各交各的断点片段,拼盘落盘归冒险应用层。 ## Core 层零 UI;随机零消费。 class_name HeroStats extends RefCounted var db: ConfigDb var journal: GainJournal # 加算日记只读引用(生效公式的加算区取数口——构造注入,宿主同源单件) var hero_hp: int = 0 # 生命账 var feats: Array[String] = [] # 特性持有集(活引用注入——关内兑付 relic 须当关生效;收束清零) var attack_type := "" # 攻击类型轴快照(空串=未注入→锻造配置默认值回退) var armor_type := "" # 甲类型轴快照(同上) var _base_override: Dictionary[Variant, Variant] = {"atk": 0, "max_hp": 0, "armor_class": 0} var _override: Array[int] = [0, 0] # 调试浮窗百分比通道 [atk_pct, hp_pct](每关新 runner 应用面) func _init(db_ref: ConfigDb, base_override_ref: Dictionary, journal_ref: GainJournal) -> void: db = db_ref journal = journal_ref _base_override = _normalize_override(base_override_ref) # 永久强化 flat 重注(单关测试模式 start_level 收 base_override_in 时调用——实例不重建,值重注) func rebase_override(raw: Dictionary) -> void: _base_override = _normalize_override(raw) # 调试浮窗百分比通道(测试工具覆盖——每关新 runner 都要应用;正式侧归元进度) func set_debug_override(atk_pct: int, hp_pct: int) -> void: _override = [atk_pct, hp_pct] # 续玩生命钳制(断点可能带出高于生效上限的账本值——恢复后钳到保守上限:表值+永久强化 flat # 后乘调试 pct、下限 1;口径不含加算区,加算区恢复后其余战斗照常。原宿主 _resume_params 的 # mini 式逐字收进) func clamp_hp_to_resume_cap(db_max_hp: int) -> void: hero_hp = mini(hero_hp, maxi(1, int(round(float(int(db_max_hp) + int(_base_override.get("max_hp", 0))) * (100.0 + float(_override[1])) / 100.0)))) # 注入参数规范化:三字段缺键/-1 = 表值兜底(→ 0 flat);负值一律 clamp 0(白板 = 全零合法态) func _normalize_override(raw: Dictionary) -> Dictionary[Variant, Variant]: var out: Dictionary[Variant, Variant] = {"atk": 0, "max_hp": 0, "armor_class": 0} for k in out.keys(): out[k] = maxi(0, int(raw.get(k, 0))) return out # ---- 生效公式(单点——叠加次序钉死:表值 → 永久强化 flat → 加算区 flat 先加, # 调试浮窗百分比后乘;pct=0 时乘法恒等——MC 锚零漂移)---- func effective_max_hp() -> int: var base: int = int(db.hero.max_hp) + int(_base_override.get("max_hp", 0)) + journal.bonus_total("max_hp") return maxi(1, int(round(float(base) * (100.0 + float(_override[1])) / 100.0))) func effective_atk() -> int: var base: int = int(db.hero.atk) + int(_base_override.get("atk", 0)) + journal.bonus_total("atk") return maxi(1, int(round(float(base) * (100.0 + float(_override[0])) / 100.0))) # 护甲等级生效值(入战 AC 三层可加:甲型基础 AC + 甲路线等级加值 + 加算区 armor_class 轴; # 无调试浮窗百分比通道,纯 flat) func effective_ac() -> int: return maxi(0, _armor_base_ac() + int(_base_override.get("armor_class", 0)) + journal.bonus_total("armor_class")) # 甲型基础 AC(三层之第一层——选中甲路线行 base_ac;未注入/缺行=锻造配置默认值路线, # 与 hero 表两轴删除前 armor_class=10 行为连续) func _armor_base_ac() -> int: var at: String = armor_type if armor_type != "" else db.forge_default_armor_type() var row: Dictionary[Variant, Variant] = db.forge_armor_route_by_type(at) if row.is_empty(): return int(db.hero.armor_class) # 表缺失防御兜底(现役基准) return int(row.get("base_ac", 0)) # 回血公式单点(恢复点节点兑付/账本置满封顶同式;算术宿主——原 LevelRunner.rest_heal 迁入) static func rest_heal(pct: int, hp: int, max_hp: int) -> int: return mini(int(floor(float(max_hp) * float(pct) / 100.0)), max_hp - hp) # 单关模式重开重置(值重置非实例重建——shop 绑定引用不换代;语义=原单关 start_level 重置面。 # 钱包/日记清零各归各件,由宿主在本调用前完成——日记先清再置满,生命口径才不含加算区) func reset_for_single_level() -> void: _base_override = {"atk": 0, "max_hp": 0, "armor_class": 0} reset_adventure_accounts() # 冒险账面重置(冒险开始/单关重开共用值面——生命置满基础上限、特性持有集清零) func reset_adventure_accounts() -> void: feats = [] hero_hp = effective_max_hp() # 特性持有集读面(效果接线/求值/摘要唯一取数口——副本防外部改写) func feats_held() -> Array: return feats.duplicate() # ---- 断点自交片段(应用层拼盘口收件——本件只交自己的账)---- # 片段键:hero_hp/relics(其余账面键归 GpWallet/GainJournal 各自片段) func breakpoint_fragment() -> Dictionary[Variant, Variant]: return { "hero_hp": hero_hp, "relics": feats.duplicate(), } # 片段回填(生命直载;特性持有集逐项归一 String——旧档防御同款) func restore_fragment(frag: Dictionary, _params: Dictionary) -> void: hero_hp = int(frag.get("hero_hp", 0)) feats = [] for r in (frag.get("relics", []) as Array): feats.append(String(r))