diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd index af34496f..f6703775 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/playtest_logger.gd @@ -118,7 +118,7 @@ func log_level_end(payload: Dictionary) -> void: func _on_step_row(row: Dictionary) -> void: _ring(_recent_steps, {"t": row.type, "src": row.source_class, "v": int(row.value), "r": int(row.round_index)}) # 结算明细行逐行落盘(2026-09-12 用户要求——五段结算完整回放数据面); - # 公共七字段之外的全部行字段(payload:roll/ac/tier/crit/normal/pierce/magic/baseline/attach/target/ + # 公共七字段之外的全部行字段(payload:roll/ac/tier/crit/normal/pierce/magic/baseline/rider/target/ # effect_id/pool_value 等)随行平铺落盘(2026-09-17 数值分析工具批——无 payload 的旧日志反事实只能 # 比例近似,新日志可精确重算)。 var line := {"kind": "step", "type": row.type, "source": row.source_class, diff --git a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd index d0f7e91e..3d407cea 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/presentation/views/battle_log_view.gd @@ -6,7 +6,10 @@ # Date: 2026-09-12(基础 = 2026-09-11 13 类行协议格式化版;2026-09-15 格面资源批 15→10 类: # 删 item / consumed_ineffective / use_rejected / window_open / window_skip 五类——可选类道具与窗口下线, # 战斗结算 行协议清单同步;2026-09-16 护甲等级掷点批:伤害行补掷点读数/档位随行标注 + nat20 金框高亮; -# 同日 敌人技能池批:施放行 = 蓄力行 charge 改型〔增益技 0 伤害施放、具名技能〕) +# 同日 敌人技能池批:施放行 = 蓄力行 charge 改型〔增益技 0 伤害施放、具名技能〕; +# 2026-09-20 八型批呈现层漏网修复(真人试玩卡断):伏击退役拆除伤害行 attach 字段后, +# 基线+伏击与普通+穿刺+魔法三分量两拆行分支未同步拆除——敌方行(带 baseline 无 attach) +# 格式化即 Invalid access,结算段中途夭折、界面卡死;两分支随批拆除,行上归因只剩掷点随行) # 高内聚边界:战斗结算 步骤流 → 玩家文案的全部格式化规则内聚于此;来源文案映射来自 SettlementTable。 class_name BattleLogView extends PanelContainer @@ -171,12 +174,8 @@ func _format_row(row: Dictionary, agg_count := -1) -> String: var body := "%s 单次%+d(池 %+d)%s" % [name_, v, pool, suffix] return prefix + (_gold_frame(body) if hl != "" else "[color=%s]%s[/color]" % [col, body]) "damage": - var extra := "" - if row.get("baseline", null) != null: - extra = "(基线 %d + 伏击 %d)" % [int(row.baseline), int(row.attach)] - elif row.get("pierce", null) != null: - # 主角攻击三分量拆行(攻击类型×护甲克制批——P2 归因:这刀里哪段是穿刺/魔法挣的) - extra = "(普通 %d + 穿刺 %d + 魔法 %d)" % [int(row.normal), int(row.pierce), int(row.magic)] + # 伤害行无行上拆行文案——基线+伏击与三分量两旧归因拆行已随伏击退役批(2026-09-20) + # 与充能退役批(2026-09-19)出协议,行上归因读数只剩掷点档位随行 # 护甲等级掷点留档(2026-09-16 掷点批——掷点读数与档位随行可读;吃掷点分量恒带,棋盘来源行不带) var roll_txt := "" if row.get("roll", null) != null: @@ -187,7 +186,7 @@ func _format_row(row: Dictionary, agg_count := -1) -> String: roll_txt += " · ×%d%%" % int(row.tier) roll_txt += "〕" var kill := " ⚔击杀!" if row.get("killed", false) else "" - var body := "%s -%d%s%s%s%s%s" % [name_, v, extra, roll_txt, tail, kill, suffix] + var body := "%s -%d%s%s%s%s" % [name_, v, roll_txt, tail, kill, suffix] var body_col := "#e07050" if row.get("killed", false) else ("#9a8a72" if v == 0 else "#e0a050") if bool(row.get("crit", false)): return prefix + _gold_frame(body) # nat 最大面暴击 = 金框高亮行 diff --git a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd index e5d53477..716f2844 100644 --- a/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd +++ b/prototypes/dungeon-sweeper-vertical-slice/smoke/smoke_all.gd @@ -81,6 +81,7 @@ func _run_core_smoke(db, rng) -> void: _smoke_difficulty_table() _smoke_damage_types() _smoke_playtest_logger() + _smoke_battle_log_view() _smoke_encounter_roll() _smoke_attack_chain_fresh_pool() _smoke_clear_reward_chain() @@ -2345,3 +2346,45 @@ func _fresh_rng(seed_val: int): var rng = load("res://core/rng_service.gd").new() rng.set_master_seed(seed_val) return rng + + +# ---- 战斗日志视图·行格式化呈现层同步闸(2026-09-20 真人试玩卡断 bug 回归:八型批伏击退役拆除 +# 伤害行 attach 字段后本视图拆行分支漏网未同步——敌方首笔攻击行带 baseline 无 attach, +# _format_row Invalid access 使结算段中途夭折、界面卡死。行协议变更必须同步呈现层〔战斗结算 §行协议〕, +# 本段把现行行形态逐型喂进真实视图:拆行残留与行字段错位在此转红)---- +func _smoke_battle_log_view() -> void: + print("--- 战斗日志视图行格式化(行协议 9 类呈现层同步闸)---") + var view = load("res://presentation/views/battle_log_view.gd").new(_db) + # 敌方行动伤害行(现行形态:baseline 在场、attach 已出协议——修复前此行即 Invalid access) + view.append_row({"type": "damage", "display_name": "地精 撕咬", "value": 2, "target": "主角", + "baseline": 3, "roll": 15, "ac": 12, "tier": 100, + "source_class": "enemy_action", "round_index": 2}) + var enemy_txt: String = view.parsed_text() + check(enemy_txt.contains("撕咬") and enemy_txt.contains("〔掷 15 vs 护甲 12"), "敌方伤害行格式化(baseline 行不读退役 attach)") + check(not enemy_txt.contains("伏击") and not enemy_txt.contains("基线"), "退役伏击/基线拆行文案不再出现") + # 主角攻击行·单分量模型(充能退役后 payload 只带实际攻击类型一键——修复前 pierce/magic 型即崩) + view.append_row({"type": "damage", "display_name": "主角攻击", "value": 4, "pierce": 4, + "roll": 20, "ac": 13, "tier": 100, "crit": true, "killed": true, + "source_class": "hero_attack", "round_index": 2}) + var hero_txt: String = view.parsed_text() + check(hero_txt.contains("暴击×") and hero_txt.contains("⚔击杀") and hero_txt.contains("■"), "主角穿刺型暴击行金框(单分量 payload 不触发三分量拆行)") + check(not hero_txt.contains(" + 穿刺 "), "退役三分量拆行文案不再出现") + # 未生效行聚合(pattern 3 聚合纪律——同键两行呈现 ×2 不逐行刷屏) + view.append_row({"type": "reward_ineffective", "display_name": "增益未生效(满层)", "batch_key": "attack_up", "source_class": "safe_cell", "round_index": 1}) + view.append_row({"type": "reward_ineffective", "display_name": "增益未生效(满层)", "batch_key": "attack_up", "source_class": "safe_cell", "round_index": 1}) + view.append_row({"type": "heal", "display_name": "急救包", "value": 3, "target": "主角", "source_class": "safe_cell", "round_index": 1}) + var agg_txt: String = view.parsed_text() + check(agg_txt.contains("⚠") and agg_txt.contains("×2"), "未生效行聚合计数 ×2 呈现") + # 其余行型逐型喂入:格式化中途夭折 = 解析文本零增长(脚本错误静默吞断言的盲区由此转为可见 FAIL) + var rest := { + "effect": {"type": "effect", "display_name": "攻击强化", "value": 1, "pool_value": 2, "effect_id": "attack_up", "source_class": "flag_hit", "round_index": 1}, + "flag_miss": {"type": "flag_miss", "display_name": "标旗未中", "source_class": "flag_miss", "round_index": 1}, + "cast": {"type": "cast", "display_name": "卓尔精灵 施放 毒雾", "source_class": "enemy_action", "round_index": 3}, + "decay": {"type": "decay", "value": 1, "round_index": 3}, + "verdict": {"type": "verdict", "display_name": "下一轮次", "value": 0, "round_index": 3}, + "highlight": {"type": "highlight", "display_name": "完美排雷!清图奖励 ×2", "value": 2, "source_class": "clear_reward", "round_index": 4}, + } + for row_type in rest: + var before: int = view.parsed_text().length() + view.append_row(rest[row_type]) + check(view.parsed_text().length() > before, "行型 %s 格式化落地(解析文本增长)" % String(row_type))