iter56-卡1:结算期文字列表退役与日志零文案(迭代56卡1): 撤B区逐行文字列表(row_pacer文字入带三调用/行格式化域删净——FX排播链零diff保留)+日志step行零文案(text/display_name不落盘)+reward_ineffective六发射点补reason结构化(full_layers/full_hp/no_enemy_alive/hero_down); 分析工具改读结构化字段(target==all_enemies/reason优先text兜底双分支)+analyze_parity_check对拍载体。读数: R1零逐行文字/R2 FX链行型分布独立推导逐位一致/R3零文案+全字段落盘/R4判读等价三读数全一致/R5四类reason逐一命中——各T提交信息载; 能力行:137/:138名实一致(_smoke_settle_zero_rows/_assert_fx_chain/_assert_no_text_and_full_payload/_assert_four_reasons); 测试削弱全部申报内(§10清单+§5.5同生死波及面11处登记实现报告); 中档审计通过

This commit is contained in:
DKChan committed 2026-11-08 03:47:33 +08:00
1 parent 9251d35650
commit 908e518306
40 files changed
+1258 -1298

No files matched your search

+2 -9
View File
@@ -249,7 +249,6 @@ func _resume_from_menu() -> void:
run_scope.level_active = true
sync_status_entry() # 续玩恢复点亮状态面板入口角标
ui.modals.run.level_screen.set_abandon_available(true)
ui.battle.log_view.clear()
logger.start_session(int(bp.get("master_seed", 0)))
run_scope.adventure.set_hero_override(ui.modals.menu.debug_overlay.atk_pct(), ui.modals.menu.debug_overlay.hp_pct())
# 同调三键随续玩下传(build_adventure_params 统一构造,与开新局同源同口径)
@@ -297,7 +296,6 @@ func _start_adventure() -> void:
run_scope.level_active = true
sync_status_entry() # 进对局点亮状态面板入口角标
ui.modals.run.level_screen.set_abandon_available(true) # 放弃入口挂选路停点(天然非战斗期)
ui.battle.log_view.clear()
var seed_val := seed_value()
logger.start_session(seed_val)
run_scope.adventure.set_hero_override(ui.modals.menu.debug_overlay.atk_pct(), ui.modals.menu.debug_overlay.hp_pct())
@@ -432,13 +430,8 @@ func pace_delay_for(prev_seg: String, next_seg: String) -> float:
func _on_round_started(ri: int) -> void:
# 轮首分隔行随排播队列走(落在前轮各行之后——带序正确);轮横幅即轮切换时刻发射
#(view 内判减动效=静态化变体)。减动效快路=分隔行同帧直落(不跳过——consume_now 复用
# _consume_row 轮首分隔行分支)。
if UiKit.motion_scale < 1.0:
ui.screens.row_pacer.consume_now({"__round_line": ri})
else:
ui.screens.row_pacer.enqueue({"__round_line": ri})
# 轮横幅即轮切换时刻发射(view 内判减动效=静态化变体);逐行文字列表退役后
# 轮首分隔行不再入队(B 区明细带承载面已撤)。
ui.battle.settlement_fx.play_round_banner(ri)
# 秘宝呈现同步:每战首轮装配链
# 显式重算〔🔴 契约复原语义:下一战装配链重算即含「未装配→写 0」复原〕。
-1
View File
@@ -51,7 +51,6 @@ static func _build_screens(root: Variant) -> void:
static func _build_battle_wiring(root: Variant, handlers: Dictionary) -> void:
# 结算行排播泵(本体在 row_pacer.gd;本类注入视图句柄与呈现刷新回调,零信号连接)
root.ui.screens.row_pacer = RowPacer.new()
root.ui.screens.row_pacer.log_view = root.ui.battle.log_view
root.ui.screens.row_pacer.stage = root.ui.battle.stage
root.ui.screens.row_pacer.board_zone = root.ui.battle.board_zone
root.ui.screens.row_pacer.settlement_fx = root.ui.battle.settlement_fx
+5 -2
View File
@@ -129,15 +129,18 @@ 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)})
# 结算明细行逐行落盘(五段结算完整回放数据面);
# 结算明细行逐行落盘(五段结算完整回放数据面);零中文文案——text/display_name 两份
# 玩家文案不落盘(日志零文案裁定 2026-11-07),未生效原因走结构化 reason 键;
# 公共七字段之外的全部行字段(payload:roll/ac/tier/crit/normal/pierce/magic/baseline/rider/target/
# effect_id/pool_value 等)随行平铺落盘(无 payload 时反事实只能
# 比例近似,带 payload 可精确重算)。
var line := {"kind": "step", "type": row.type, "source": row.source_class,
"cell": row.origin_cell, "round": int(row.round_index),
"text": String(row.display_name), "value": int(row.value),
"value": int(row.value),
"highlight": String(row.get("highlight", ""))}
for k in row.keys():
if k == "display_name":
continue # 日志零文案:display_name 平铺跳过(toast 等呈现面消费,日志不落)
if not line.has(k):
line[k] = row[k]
_append(line)
+5 -11
View File
@@ -1,20 +1,19 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 结算行排播泵:逻辑先行零回压,行按段分组阶梯延迟进明细带。
## 硬契约:只做信号的后置消费——结算顺序/结果/时序逻辑零改动;减动效开=延迟归零同帧直落,行呈现不跳过。
## 开模态前必须 flush()——明细回看/停点/终局面所见明细带恒完整。
## 结算行排播泵:逻辑先行零回压,行按段分组阶梯延迟驱动演出链(伤害飘字/弹道/站桩反馈)。
## 硬契约:只做信号的后置消费——结算顺序/结果/时序逻辑零改动;减动效开=延迟归零同帧直落,演出不跳过。
## 开模态前必须 flush()——停点/终局面演出链逐行消费恒完整。
## 视图句柄与 refresh_request/pump_ended 回调由组合根注入——本类零信号连接。
extends Node
const PACE_SEGMENT_GAP := 0.3 # 段间阶梯延迟(取值须落白名单 0.2–0.4s 档位带内)
const PACE_ROW_GAP := 0.22 # 段内间隔(三轮 #3 上调 0.06→0.22——逐条演出可感知,配合数字浮现/状态脉冲)
var _rows: Array[Variant] = [] # 待排播行(step_row 载荷原样入队;__round_line 标记=轮首分隔行)
var _rows: Array[Variant] = [] # 待排播行(step_row 载荷原样入队)
var _pace_wait := 0.0 # 距下一行排播的剩余秒(_process 消耗)
var _prev_segment := "" # 上一排播行的段型(段间/段内延迟判据)
var _was_pumping := false # 排播活跃边沿(T17——末行消费帧触发结算视觉回挖掘态刷新)
# 组合根注入面(_build_ui 装配——呈现句柄 + 演播完边沿的呈现刷新回调)
var log_view: BattleLogView
var stage: BattleStageView
var board_zone: BoardZoneView
var settlement_fx: SettlementFxView
@@ -79,13 +78,8 @@ func _process(delta: float) -> void:
_prev_segment = seg
# 行消费(排播队列的出队步 / 减动效快路共用):明细带入带+演出链+呈现刷新
# 行消费(排播队列的出队步 / 减动效快路共用):演出链+呈现刷新
func _consume_row(row: Dictionary) -> void:
if row.has("__round_line"):
log_view.finish_settlement_round() # 上轮摘要行(明细带 pattern 3)随轮首分隔行落带
log_view.append_bb("[color=#e0b860]── 第 %d 轮 ──[/color]" % int(row["__round_line"]))
return
log_view.append_row(row)
stage.on_step_row_fx(row)
_play_cross_zone_fx(row) # 跨区因果演出(结算演出链批):开格之因 → 站桩之果
_play_settlement_mark(row) # 三轮 #3——逐条标出:Buff/Debuff 色环脉冲 → 伤害/治疗数字浮现
+2 -208
View File
@@ -1,21 +1,16 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## B 区结算明细带——战斗结算步骤流到玩家文案的全部行格式化规则内聚于此(挖掘期收起 1 行上轮摘要、结算期实时流)。
## B 区结算明细带壳——结算期逐行文字列表退役后保留挖掘期摘要位与冒险流程叙述行承载(append_bb)。
class_name BattleLogView
extends PanelContainer
const AGG_TYPES := ["reward_ineffective"]
const COL_GOLD := UiKit.HEX_GOLD
var _db
var _band_h_dig := 0
var _band_h_settle := 0
var _log: RichTextLabel
var _summary: Label
var _hint: Label
var _mode := "dig" # dig = 收起摘要 / settle = 实时流(模式归组合根驱动)
var _round_rows: Array[String] = [] # 本结算轮行缓冲(上轮摘要构建源——append 的是 _format_row 格式化产物 String)
var _pending_agg := {} # 聚合计数行 pending(type+batch_key → {row, count})
var _mode := "dig" # dig = 收起摘要 / settle = 展开空带(模式归组合根驱动;逐行流退役后 settle 态无逐行内容)
func _init(db_ref, layout: BattleLayoutTable) -> void:
@@ -64,213 +59,12 @@ func mode() -> String:
func clear() -> void:
_log.clear()
_round_rows.clear()
_pending_agg = {}
_summary.text = ""
# 每结算轮结束(下一轮开始/终局)——刷新挖掘期"上轮摘要"行
func finish_settlement_round() -> void:
_flush_pending_agg()
if _round_rows.is_empty():
return
var parts: Array[String] = []
for bb in _round_rows:
parts.append(_strip_bb(str(bb)))
var text := "上轮: " + " | ".join(parts)
if text.length() > 190:
text = text.substr(0, 190) + "…"
_summary.text = text
_round_rows.clear()
func append_bb(bb: String) -> void:
_flush_pending_agg()
_log.append_text(bb + "\n")
func append_row(row: Dictionary) -> void:
if AGG_TYPES.has(String(row.get("type", ""))):
_accumulate_agg(row)
return
_flush_pending_agg()
var bb := _format_row(row)
_round_rows.append(bb)
_log.append_text(bb + "\n")
func parsed_text() -> String:
return _log.get_parsed_text()
# 无点击回看入口——冒险中不给数据回看(归「生涯」06 登记);带保留单行摘要显示
# ---- 聚合计数(未生效类行防逐行惩罚轰炸——pattern 3 聚合纪律) ----
func _accumulate_agg(row: Dictionary) -> void:
var key := "%s|%s" % [String(row.get("type", "")), String(row.get("batch_key", ""))]
if _pending_agg.has(key):
_pending_agg[key]["count"] = int(_pending_agg[key]["count"]) + 1
else:
_flush_pending_agg()
_pending_agg[key] = {"row": row, "count": 1}
func _flush_pending_agg() -> void:
if _pending_agg.is_empty():
return
for key in _pending_agg:
var item: Dictionary[Variant, Variant] = _pending_agg[key]
var row: Dictionary[Variant, Variant] = item["row"]
var count: int = item["count"]
var bb := _format_row(row, count)
_round_rows.append(bb)
_log.append_text(bb + "\n")
_pending_agg = {}
# ---- 行格式化(模板「{行为} {值} → {目标}(来源)」) ----
func _source_label(src_key: String) -> String:
if src_key == "":
return ""
return String(_db.combat.settlement.core.source_label_map.get(src_key, src_key))
func _gold_frame(text: String) -> String:
# highlight 金框强调(形状+色彩双通道:■ 前缀 + 金底)
return "[bgcolor=%s][color=#%s]■ %s[/color][/bgcolor]" % [COL_GOLD, UiKit.HEX_ON_GOLD, text]
func _format_row(row: Dictionary, agg_count := -1) -> String:
var src := _source_label(String(row.get("source_class", "")))
var round_no: int = int(row.get("round_index", 0))
var prefix := " [color=#%s]R%d%s[/color] " % [UiKit.HEX_LOG_ROUND, round_no, ("·" + src) if src != "" else ""]
var suffix := "(%s)" % src if src != "" else ""
var kind := String(row.get("type", ""))
var body := ""
match kind:
"effect":
body = _fmt_effect(row, prefix, suffix)
"damage":
body = _fmt_damage(row, prefix, suffix)
"heal", "cast", "decay":
body = _fmt_simple(kind, row, prefix, suffix)
"reward_ineffective":
body = _fmt_ineffective(row, prefix, suffix, agg_count)
"seal_miss":
body = _fmt_seal_miss(row, prefix)
"verdict":
body = _fmt_verdict(row)
_:
body = _fmt_default(row, prefix, suffix)
return body
## 简单行域(heal/cast/decay 三型——单行单墨直排)
func _fmt_simple(kind: String, row: Dictionary, prefix: String, suffix: String) -> String:
var name_: String = String(row.get("display_name", ""))
match kind:
"heal":
return prefix + "[color=#%s]%s +%d%s%s[/color]" % [UiKit.HEX_LOG_HEAL, name_, int(row.get("value", 0)), _tail_of(row), suffix]
"cast":
# 施放行:增益技 0 伤害施放,具名技能
return prefix + "[color=#%s]✦ %s%s[/color]" % [UiKit.HEX_LOG_CAST, name_, suffix]
"decay":
return prefix + "[color=#%s]状态衰减 ×%d[/color]" % [UiKit.HEX_LOG_DECAY, int(row.get("value", 0))]
return ""
## 默认行域(未知型/高亮星标——未知型=名行直排)
func _fmt_default(row: Dictionary, prefix: String, suffix: String) -> String:
var name_: String = String(row.get("display_name", ""))
if String(row.get("type", "")) == "highlight":
return prefix + _gold_frame("★ %s" % name_)
return prefix + name_ + suffix
## 尾注域:目标格指向(行文附加)
func _tail_of(row: Dictionary) -> String:
var target := String(row.get("target", ""))
return " → %s" % target if target != "" else ""
## GP 型效果行域:「GP 袋 +3」兑付反馈——value = 本行 ±power,
## escrow = 战斗内缓冲现值(入钱包在战斗末一次上抛——呈现语义与增益池分账)
func _fmt_effect(row: Dictionary, prefix: String, suffix: String) -> String:
var v: int = int(row.get("value", 0))
var hl := String(row.get("highlight", ""))
var name_: String = String(row.get("display_name", ""))
if String(row.get("effect_id", "")) in ["gp_gain", "gp_loss"]:
var gp_col := UiKit.HEX_LOG_GAIN if v > 0 else UiKit.HEX_LOG_LOSS
var gp_body := "%s %+d(战斗暂存 %+d)%s" % [name_, v, int(row.get("escrow", 0)), suffix]
return prefix + (_gold_frame(gp_body) if hl != "" else "[color=%s]%s[/color]" % [gp_col, gp_body])
# 效果行 value = 单次贡献值,pool_value = 池合成值——禁相加呈现(战斗结算 呈现细则)
var pool: int = int(row.get("pool_value", 0))
var col := UiKit.HEX_LOG_HEAL if v > 0 else UiKit.HEX_LOG_LOSS
var body := "%s 单次%+d(池 %+d)%s" % [name_, v, pool, suffix]
return prefix + (_gold_frame(body) if hl != "" else "[color=%s]%s[/color]" % [col, body])
## 伤害行域:行上归因读数只剩掷点档位随行(护甲等级掷点留档——掷点读数与档位随行可读;
## 吃掷点分量恒带,棋盘来源行不带)。伤害数字不明显——伤害值放大加色独立于行文(朱红大一号)
func _fmt_damage(row: Dictionary, prefix: String, suffix: String) -> String:
var v: int = int(row.get("value", 0))
var name_: String = String(row.get("display_name", ""))
var tail := _tail_of(row)
var roll_txt := ""
if row.get("roll", null) != null:
roll_txt = "〔掷 %d vs 护甲 %d" % [int(row.roll), int(row.ac)]
if bool(row.get("crit", false)):
roll_txt += " · 暴击×%d" % int(_db.combat.settlement.damage_roll.crit_multiplier)
elif int(row.tier) < 100:
roll_txt += " · ×%d%%" % int(row.tier)
roll_txt += "〕"
var kill := " ⚔击杀!" if row.get("killed", false) else ""
var body := "%s [color=#%s][size=17]-%d[/size][/color]%s%s%s%s" % [name_, UiKit.HEX_LOG_DMG_NUM, v, roll_txt, tail, kill, suffix]
if bool(row.get("crit", false)):
return prefix + _gold_frame(body) # nat 最大面暴击 = 金框高亮行
var body_col := UiKit.HEX_LOG_DMG_KILL if row.get("killed", false) else (UiKit.HEX_LOG_ROUND if v == 0 else UiKit.HEX_LOG_DMG_BODY)
return prefix + "[color=%s]%s[/color]" % [body_col, body]
## 未生效行域:⚠ icon + 划线形状冗余(非色相独载)+ 聚合计数
func _fmt_ineffective(row: Dictionary, prefix: String, suffix: String, agg_count: int) -> String:
var name_: String = String(row.get("display_name", ""))
var n := (" ×%d" % agg_count) if agg_count > 1 else ""
return prefix + "[s][color=#%s]⚠ %s%s[/color][/s]%s" % [UiKit.HEX_LOG_VOIDED, name_, n, suffix]
## 封印落空行域:完美破/首失豁免两注记分档(金框=破完美/普通墨=豁免)
func _fmt_seal_miss(row: Dictionary, prefix: String) -> String:
var hl := String(row.get("highlight", ""))
var name_: String = String(row.get("display_name", ""))
var miss_note := ""
if hl == "perfect_broken":
miss_note = "(本图完美封印已破——清图奖励不再加倍)"
elif hl == "":
miss_note = "(首次封印落空已豁免——不计入完美封印判定)"
var body := "✗ %s%s" % [name_, miss_note]
return prefix + (_gold_frame(body) if hl == "perfect_broken" else "[color=#%s]%s[/color]" % [UiKit.HEX_LOG_ROUND, body])
## 裁定行域:胜金/负绿双墨(粗体菱形头)
func _fmt_verdict(row: Dictionary) -> String:
var name_: String = String(row.get("display_name", ""))
if int(row.value) == 1:
return "[color=#%s][b]◆ %s[/b][/color]" % [UiKit.HEX_LOG_WIN, name_]
return "[color=#%s][b]◆ %s[/b][/color]" % [UiKit.HEX_LOG_GAIN, name_]
func _strip_bb(bb: String) -> String:
var out := ""
var in_tag := false
for ch in bb:
if ch == "[":
in_tag = true
elif ch == "]":
in_tag = false
elif not in_tag:
out += ch
return out.replace(" ", " ").strip_edges()