diff --git a/game/core/adventure_runner.gd b/game/core/adventure_runner.gd index 09bc6b89..d77421c0 100644 --- a/game/core/adventure_runner.gd +++ b/game/core/adventure_runner.gd @@ -113,7 +113,7 @@ func start_adventure(seed_val: int, params: Dictionary = {}) -> bool: _base_override = _normalize_override(params.get("hero_override", {})) _first_script = bool(params.get("first_script", false)) # 缺省 = 非首次(抽签路径;真实侧由呈现层读存档布尔位传入) _aux_unlocked = params.get("aux_unlocked", {}) if params.get("aux_unlocked", {}) is Dictionary else {} # additive(迭代 7 T5) - ledger["hero_hp"] = int(db.hero.max_hp) + int(_base_override["max_hp"]) # 生命账本置满(表值+永久强化 flat——加算区刚归零) + ledger["hero_hp"] = effective_max_hp() # 生命账本置满(生效上限口径——T17 修复:含 TunePanel pct,与战斗内同源) state = "running" return _start_current_level() @@ -211,6 +211,15 @@ func set_hero_override(atk_pct: int, hp_pct: int) -> void: _runner.set_hero_override(atk_pct, hp_pct) +# 冒险账本侧生效上限(T17 修复——账本置满/回血封顶与战斗内上限同式同源): +# (表值 + 永久强化 flat + 加算区 flat)后乘 TunePanel 百分比——叠加次序与 LevelRunner.effective_max_hp +# 逐字一致。旧口径两处只算 flat 不乘 pct:调生命上限 26→104 时开局血仍 26(账本 26 / 战斗内 104)、 +# 通关 20% 回血封顶按 26 算。pct=0(模拟/冒烟默认)时乘法恒等——MC 锚零漂移。 +func effective_max_hp() -> int: + var base: int = int(db.hero.max_hp) + int(_base_override.get("max_hp", 0)) + int(bonus.get("max_hp", 0)) + return maxi(1, int(round(float(base) * (100.0 + float(_override[1])) / 100.0))) + + # 断点承载注入(迭代 8 卡①——组合根冒险开始时调用;模拟/冒烟不注入 = 断点面整体静默) func set_save_manager(sm) -> void: _save_manager = sm @@ -609,7 +618,7 @@ func _on_level_finished(payload: Dictionary) -> void: # 通关兑付(非末关恰一次;迭代 10 三层奖励框架):部分回血先行 → 加权洗牌掷 3 → 三选一停点 #(顺序即契约——回血按当刻账本与上限结算,选择停点零自动推进) var hp_before := int(ledger.get("hero_hp", 0)) - var max_eff := int(db.hero.max_hp) + int(_base_override["max_hp"]) + int(bonus.get("max_hp", 0)) + var max_eff := effective_max_hp() # T17 修复:回血封顶口径含 TunePanel pct(同战斗内上限) var heal := mini(int(floor(float(max_eff) * float(int(db.adventure.clear_heal_pct)) / 100.0)), max_eff - hp_before) ledger["hero_hp"] = hp_before + heal # 置位先于 emit(信号同步派发——处理器内立即确认时 _pending_next_level 必须已就位) diff --git a/game/presentation/views/level_map_screen.gd b/game/presentation/views/level_map_screen.gd index c11a005a..9b58f4ef 100644 --- a/game/presentation/views/level_map_screen.gd +++ b/game/presentation/views/level_map_screen.gd @@ -394,6 +394,9 @@ func _draw_graph(canvas: Control) -> void: var visited: Array = _runner.visited_ids() var cand_ids: Array = _runner.next_candidate_ids() # 连线(通道)先行画(方块覆盖其上);悬停预承诺 = 所属通道再增亮(加粗) + # T17 修复:选路停点(候选非空)非候选通道降透明——金岔路从结构网分离(干线共线重叠缺陷的 + # 视觉主因:不同源通道共用同一垂直干线,金边与石色边焊接成「整列可走」假象) + var dim_passages: bool = not cand_ids.is_empty() for node in nodes: var from: Vector2 = pos[node] for tid in _out_ids(node, nodes): @@ -403,7 +406,7 @@ func _draw_graph(canvas: Control) -> void: var to: Vector2 = pos[target] var lit: bool = _id_of(node) == current_id and cand_ids.has(tid) var hovered: bool = lit and _hover_id == tid - var col := COL_PASSAGE_LIT if lit else COL_PASSAGE + var col := COL_PASSAGE_LIT if lit else (Color(COL_PASSAGE, 0.35) if dim_passages else COL_PASSAGE) var width := 4.0 if hovered else (3.0 if lit else 2.0) # 样张 v1 冻结:2/3/4(原 1.5 取整) _draw_passage(canvas, from, to, col, width) # 房间方块 @@ -511,6 +514,9 @@ func _draw_passage(canvas: Control, from: Vector2, to: Vector2, col: Color, widt var b := to - Vector2(NODE_HALF.x, 0) if b.x < a.x: return - var mid_x := (a.x + b.x) / 2.0 + # T17 修复:垂直干线 x 按源节点 y 偏移——不同源的边不再共线重叠(旧 mid_x=中点使相邻层 + # 全部通道焊成一条干系,「金岔路+整列可走」误读的直接几何根源)。同源双边仍共线(同出发 + # 点语义正确),跨源分离度 = 源行距 × 0.2(L2 行距 ~58px → 偏移 ~12px 可辨)。 + var mid_x := (a.x + b.x) / 2.0 + (a.y - b.y) * 0.2 var pts := PackedVector2Array([a, Vector2(mid_x, a.y), Vector2(mid_x, b.y), b]) canvas.draw_polyline(pts, col, width) diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index e81fe505..7b8bcba4 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -347,13 +347,14 @@ func _smoke_adventure() -> void: var hp_before: int = int(p.get("hp_before", -1)) var hp_after: int = int(p.get("hp_after", -1)) # 模拟档 v3.4 商店贪心购血合法抬升(L2/L3 店在 Boss 前)——回血口径窗口 = 加算区件 + 购血余量 0..15 + # T17 账本口径修复随批:置满/回血封顶改生效上限口径(flat 后乘 TunePanel pct)——窗口同步 ×5(本段 pct=400) var heal_ok_i := false for extra in 16: - var m := int(_db.hero.max_hp) + hp_acc + extra + var m := int(round(float(int(_db.hero.max_hp) + hp_acc + extra) * 5.0)) if heal == mini(int(floor(float(m) * float(heal_pct) / 100.0)), m - hp_before): heal_ok_i = true break - if not heal_ok_i or hp_after != hp_before + heal or hp_after > int(_db.hero.max_hp) + hp_acc + 15: + if not heal_ok_i or hp_after != hp_before + heal or hp_after > int(round(float(int(_db.hero.max_hp) + hp_acc + 15) * 5.0)): heal_ok = false ks_prefix += int(_db.meta_boss_keystone_row(int(p.get("level_index", -1))).get("first_kill", 0)) if int(p.get("keystone_gained", -1)) != int(_db.meta_boss_keystone_row(int(p.get("level_index", -1))).get("first_kill", 0)) or int(p.get("keystone_total", -1)) != ks_prefix: @@ -486,7 +487,7 @@ func _smoke_adventure() -> void: var expect_acc: int = acc_shop + (int(_db.adventure.bonus_max_hp_flat) if picked2 == "max_hp" else 0) check(int(a8.bonus.get("max_hp", -1)) == expect_acc, "三选一属性件与商店购买同账累加(max_hp=%d = %d + 三选一%s)" % [int(a8.bonus.get("max_hp", -1)), acc_shop, picked2]) - var max_eff8 := int(_db.hero.max_hp) + int(a8.bonus["max_hp"]) + var max_eff8: int = a8.effective_max_hp() # T17 口径修复随批:账本生效上限含 TunePanel pct(a8 已 set 400) check(int(a8.ledger["hero_hp"]) <= max_eff8, "通关部分回血封顶(hp %d ≤ 生效上限 %d——血回满语义退役)" % [int(a8.ledger["hero_hp"]), max_eff8]) check(a8.continue_to_next_level(), "兑付后推进受理(下一关开始)")