119 lines
5.8 KiB
GDScript
119 lines
5.8 KiB
GDScript
## 主角数值账:冒险生命周期主角数值域唯一所有者(生命账与特性持有集、锻造强化快照与调试浮窗
|
|
## 百分比通道、攻击/甲类型轴快照、生效上限/攻击/护甲公式与回血封顶公式单点、续玩生命钳制收口)。
|
|
## 生效公式消费加算日记域汇总(注入 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))
|