feat(slice): 措辞收口+数值对齐+场景一/三拍板落地批——①registry 指路化收口:主角条目去 MVP 值改读 hero.tres、技能条目七行枚举(补收撕咬/毒爆)+倍率冷却保底指路 skills.tres、敌人条目五敌种枚举+池权重 EV 读数指路 enemy.tres、charge_grants/equivalence_ref_damage/board_gen 参考行删旧值改指路、中点锚改 floor 向下取整口径(与切片校验式对齐);②刷新临时名全链退役:refresh_max_per_round→refresh_cap_per_round、request_refresh_active→request_refresh、can_request_refresh→can_refresh(8 文件 grep 零残留)+行动经济「字段名对齐待批」注记收口;③五区残留清零:玩家可见副标题改战斗屏四区、build_five_zone→build_battle_ui、探针文案/注释 11 处;④行动经济 schema 默认值 8/8→6/6 随压速批+明细字号抬标 14px(HUD框架可访问性下限)+RAP−8 字面量 6 处改表值驱动(ap_bar_view.set_refresh_cost 装配注入通道,toast 同步动态化);⑤意图徽标三档占位(普攻—/伤害技⚔/增益技✦/重击类蓄力)——技能名退出徽标堵剧透(2026-09-18 用户拍板,终稿动画三档归样张后,美术清单 §2.3 在案);⑥R1 进度解析口径修订(数据配置 v0.4 用户两段拍板):ceil 向上取整保证偏难+超出最大行封顶取最大值+域越界 Fatal 诊断进程退出码 1(config_db _ceil_key/_fatal 通道+config_fatal_probe 探针实测退出码 1+冒烟 ceil/clamp 两断言)+registry enemy_table 进度键 ceil 同步;⑦tune 探针断言表值驱动修复(旧字面量 6/50、12/75 随数值压缩批失效,改读 db 基线派生);⑧06 待办四条目(意图徽标占位小修已解决/分辨率档位 1280×800+1920×1080 定案/变异对初始展开影响语义挂起)+美术资源依赖清单成文(场景一产物);冒烟 296 全绿+ui/click/layout/tune 四探针 PASS+fatal 探针退出码 1(伤害道具批、护甲重配批、数值纪律收口批归并行会话 a2fc39f/cdc226e 在先提交,行动经济改名 hunks 本批全链收口)
This commit is contained in:
1 parent
a2fc39ff17
commit
0da19a5b09
24 files changed
+346
-115
No files matched your search
@@ -100,7 +100,7 @@ func request_stop() -> bool:
|
||||
# 轮内刷新(2026-09-12 方案 A 用户拍板;等待期通道 2026-09-15 用户拍板整体移除):唯一入口 =
|
||||
# Active 相位——仅扣 RAP,相位与 AP 均不动。刷新不打断轮次、不触发结算;换图编排归 战斗结算
|
||||
# (retire + install 立即执行,玩家同轮继续挖新图)。决策主体 = 玩家(经 HUD框架发起)纪律不变。
|
||||
func request_refresh_active() -> bool:
|
||||
func request_refresh() -> bool:
|
||||
if phase != PHASE_ACTIVE:
|
||||
_reject("phase")
|
||||
return false
|
||||
|
||||
@@ -51,7 +51,7 @@ var _facts_reveals: Array = []
|
||||
var _facts_flags: Array = []
|
||||
var _facts_path := ""
|
||||
var _settling := false
|
||||
var _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_max_per_round)
|
||||
var _refresh_used_this_round := 0 # 每轮刷新计数(Active 相位合计,兜底上限 = ActionEconomyTable.refresh_cap_per_round)
|
||||
var _board_seq := 0 # 板序号(本战斗第几张板——快照归因用)
|
||||
var _board_from_midround_refresh := false # 装板 provenance:轮内刷新换来的图 = true(清图奖励守卫——刷新图不发清图奖励)
|
||||
var _flag_consumed := {} # 兑付资格以雷格实例为键(板级;换图重置)
|
||||
@@ -688,9 +688,9 @@ func _exit_distribution() -> void:
|
||||
|
||||
|
||||
# 呈现层查询面(HUD 刷新按钮可用性)——挖掘期唯一通道
|
||||
func can_request_refresh() -> bool:
|
||||
func can_refresh() -> bool:
|
||||
return battle_result == "" \
|
||||
and _refresh_used_this_round < int(db.action_economy.refresh_max_per_round) \
|
||||
and _refresh_used_this_round < int(db.action_economy.refresh_cap_per_round) \
|
||||
and action_economy.phase == action_economy.PHASE_ACTIVE \
|
||||
and action_economy.rap >= int(db.action_economy.rap_refresh_cost)
|
||||
|
||||
@@ -699,9 +699,9 @@ func can_request_refresh() -> bool:
|
||||
# 挖掘期唯一通道(2026-09-12 方案 A 拍板 + 2026-09-15 等待期通道整体移除):Active 相位立即换图
|
||||
# 继续挖(RAP -8;AP 保留、相位不动、不触发结算);非挖掘期请求一律拒绝——非清图轮恒同图。
|
||||
func player_request_refresh() -> bool:
|
||||
if not can_request_refresh():
|
||||
if not can_refresh():
|
||||
return false
|
||||
if not action_economy.request_refresh_active():
|
||||
if not action_economy.request_refresh():
|
||||
return false
|
||||
_refresh_used_this_round += 1
|
||||
_reward_economy.retire_mapping() # 旧图批次退役(地图战斗资源 Ready→retired;E6 新批重置前提)
|
||||
@@ -725,7 +725,7 @@ func _battle_over(result: String) -> void:
|
||||
# ---- 呈现状态面(E1 封装收口 2026-09-13):呈现层 / headless 自动玩家的唯一读取出口——
|
||||
# 协作对象(_enemy_system/_status_effects/_reward_economy)私有化后,跨层读取一律经本快照或下列命名面(diagnostic_snapshot 同款模式,
|
||||
# 禁再链式穿透 settlement.enemy_system.hero 一类路径)。载荷 schema 成文(code-conventions 2.1):
|
||||
# round / battle_result / phase / ap / rap / can_request_refresh / hero{hp,max_hp,atk,alive,armor_class} /
|
||||
# round / battle_result / phase / ap / rap / can_refresh / hero{hp,max_hp,atk,alive,armor_class} /
|
||||
# enemies[{order,display_name,hp,max_hp,enemy_id,armor_type,armor_class,pending_skill,pending_type,pending_name,effects[],ambush_layers,alive}] /
|
||||
# (敌人技能池批批 2 定稿:beat/ring_size/ring_index 拆除 → pending_* 三字段 + 敌方增益 effects 投影;池与权重不公开)
|
||||
# ambush_layers / hero_effects[](增益状态 query_active 投影)
|
||||
@@ -769,7 +769,7 @@ func presentation_snapshot() -> Dictionary:
|
||||
"battle_result": battle_result,
|
||||
"phase": String(action_economy.phase),
|
||||
"ap": int(action_economy.ap), "rap": int(action_economy.rap),
|
||||
"can_request_refresh": can_request_refresh(),
|
||||
"can_refresh": can_refresh(),
|
||||
"hero": hero,
|
||||
"enemies": enemies,
|
||||
"ambush_layers": ambush,
|
||||
|
||||
@@ -42,10 +42,21 @@ var _reload_gate := false # 数据配置 EC8——战斗结
|
||||
|
||||
func push_diag(severity: String, category: String, message: String) -> void:
|
||||
_diagnostics.append({"severity": severity, "category": category, "message": message})
|
||||
if severity == "error":
|
||||
push_error("[ConfigDB/%s] %s" % [category, message])
|
||||
elif severity == "warning":
|
||||
if severity == "warning":
|
||||
push_warning("[ConfigDB/%s] %s" % [category, message])
|
||||
else:
|
||||
push_error("[ConfigDB/%s] %s" % [category, message])
|
||||
|
||||
|
||||
# 运行期数据致命(2026-09-18 用户拍板:越域记 Fatal 级日志后立即退出——数据配置通道 fail-fast 纪律补齐到查询期)
|
||||
func _fatal(category: String, message: String) -> void:
|
||||
push_diag("fatal", category, message)
|
||||
push_error("[ConfigDB/%s] FATAL: %s —— 数据配置致命,进程退出" % [category, message])
|
||||
var main_loop := Engine.get_main_loop()
|
||||
if main_loop is SceneTree:
|
||||
main_loop.quit(1) # 帧末退出,退出码 1——调用方须立即 return,勿再消费返回值
|
||||
else:
|
||||
OS.crash("ConfigDB/%s fatal: %s" % [category, message])
|
||||
|
||||
|
||||
func reset_diagnostics() -> void:
|
||||
@@ -188,7 +199,7 @@ func validate_skill_pool_links(skill_table, enemy_table) -> Array:
|
||||
return errs
|
||||
|
||||
|
||||
# ---- R1 难度行解析(floor + warning 四元组去重;请求越域致命)----
|
||||
# ---- R1 难度行解析(2026-09-18 用户拍板修订:ceil 向上取整保证偏难;域越界与内容缺口 Fatal 退出)----
|
||||
|
||||
func resolve_board_gen(L: int, D: int) -> Dictionary:
|
||||
return _resolve_progress(board_gen.rows, L, D, "board_gen")
|
||||
@@ -213,66 +224,65 @@ func resolve_difficulty_modifiers(L: int, D: int) -> Dictionary:
|
||||
|
||||
func _resolve_progress(rows: Array, L: int, D: int, table_id: String) -> Dictionary:
|
||||
if L < 1 or D < 0:
|
||||
push_diag("error", "request_oob", "%s 请求越域 (%d,%d)" % [table_id, L, D])
|
||||
_fatal("request_oob", "%s 请求越域 (%d,%d)——进度坐标低于合法域(关卡序号≥1、节点深度≥0)" % [table_id, L, D])
|
||||
return {}
|
||||
var levels := {}
|
||||
var level_keys: Array = []
|
||||
for row in rows:
|
||||
levels[int(row.get("level_index", 0))] = true
|
||||
var level_keys := levels.keys()
|
||||
var lv := int(row.get("level_index", 0))
|
||||
if not level_keys.has(lv):
|
||||
level_keys.append(lv)
|
||||
if level_keys.is_empty():
|
||||
_fatal("structural", "%s 行集为空——锚行校验应已拦截" % table_id)
|
||||
return {}
|
||||
level_keys.sort()
|
||||
var l_star: int
|
||||
var mt_level := "exact"
|
||||
if L < level_keys[0]:
|
||||
l_star = level_keys[0]
|
||||
mt_level = "ceil"
|
||||
else:
|
||||
l_star = level_keys[0]
|
||||
for lv in level_keys:
|
||||
if lv <= L:
|
||||
l_star = lv
|
||||
if l_star < L:
|
||||
mt_level = "floor"
|
||||
var depth_keys := {}
|
||||
var l_star := _ceil_key(level_keys, L)
|
||||
var l_clamped := l_star < 0
|
||||
if l_clamped:
|
||||
l_star = int(level_keys[-1]) # 超出最大行 → 取最大行封顶(2026-09-18 追加拍板:可取范围内向上取整,超出用最大值)
|
||||
var depth_keys: Array = []
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star:
|
||||
depth_keys[int(row.get("node_depth", 0))] = true
|
||||
var d_keys := depth_keys.keys()
|
||||
d_keys.sort()
|
||||
var d_star: int
|
||||
var mt_depth := "exact"
|
||||
if d_keys.is_empty():
|
||||
push_diag("error", "structural", "%s level %d 无行(锚行校验应已拦截)" % [table_id, l_star])
|
||||
return {}
|
||||
if D < d_keys[0]:
|
||||
d_star = d_keys[0]
|
||||
mt_depth = "floor"
|
||||
else:
|
||||
d_star = d_keys[0]
|
||||
for dv in d_keys:
|
||||
if dv <= D:
|
||||
d_star = dv
|
||||
if d_star < D:
|
||||
mt_depth = "floor"
|
||||
var dv := int(row.get("node_depth", 0))
|
||||
if not depth_keys.has(dv):
|
||||
depth_keys.append(dv)
|
||||
depth_keys.sort()
|
||||
var d_star := _ceil_key(depth_keys, D)
|
||||
var d_clamped := d_star < 0
|
||||
if d_clamped:
|
||||
d_star = int(depth_keys[-1])
|
||||
var missing := ""
|
||||
if mt_level == "ceil":
|
||||
missing = "base_ceil"
|
||||
elif mt_level == "floor" and mt_depth == "floor":
|
||||
missing = "both"
|
||||
elif mt_level == "floor":
|
||||
missing = "level_floor"
|
||||
elif mt_depth == "floor":
|
||||
missing = "depth_floor"
|
||||
if l_clamped and d_clamped:
|
||||
missing = "both_clamp"
|
||||
elif l_clamped:
|
||||
missing = "level_clamp"
|
||||
elif d_clamped:
|
||||
missing = "depth_clamp"
|
||||
elif l_star != L and d_star != D:
|
||||
missing = "both_ceil"
|
||||
elif l_star != L:
|
||||
missing = "level_ceil"
|
||||
elif d_star != D:
|
||||
missing = "depth_ceil"
|
||||
if missing != "":
|
||||
var dedup_key := "%s|%s|%d|%d" % [table_id, missing, l_star, d_star]
|
||||
if not _warn_dedup.has(dedup_key):
|
||||
_warn_dedup[dedup_key] = true
|
||||
push_diag("warning", "resolve_" + missing, "%s 请求 (%d,%d) floor 解析 → (%d,%d) [%s]" % [table_id, L, D, l_star, d_star, missing])
|
||||
var verb := "clamp 解析(超出最大行取最大值)" if missing.contains("clamp") else "ceil 解析(向上取整保证偏难)"
|
||||
push_diag("warning", "resolve_" + missing, "%s 请求 (%d,%d) %s → (%d,%d) [%s]——2026-09-18 拍板" % [table_id, L, D, verb, l_star, d_star, missing])
|
||||
for row in rows:
|
||||
if int(row.get("level_index", 0)) == l_star and int(row.get("node_depth", 0)) == d_star:
|
||||
return row
|
||||
return {}
|
||||
|
||||
|
||||
# 取 sorted_keys 中第一个 >= request 的键;无(请求高于最大行)返回 -1 → 调用方 Fatal
|
||||
func _ceil_key(sorted_keys: Array, request: int) -> int:
|
||||
for k in sorted_keys:
|
||||
if int(k) >= request:
|
||||
return int(k)
|
||||
return -1
|
||||
|
||||
|
||||
# ---- 地形变异 variant_pool_gate:候选池门控(∅ biome_tag = 通配)----
|
||||
|
||||
func variant_pool(level_index: int, board_biomes: Array) -> Array:
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
class_name ActionEconomyTable
|
||||
extends Resource
|
||||
|
||||
@export var ap_per_round: int = 8 # [1,32] knob
|
||||
@export var ap_per_round: int = 6 # [1,32] knob(默认值 = 现行表值基线,2026-09-16 AP 压速批 8→6 随动)
|
||||
@export var dig_ap_cost: int = 1 # [1,8] knob(域下限 1 = 零预算系统失效拒绝)
|
||||
@export var flag_place_ap_cost: int = 1 # [0,8] knob
|
||||
@export var flag_pull_ap_cost: int = 1 # [0,8] knob
|
||||
@export var rap_refresh_cost: int = 8 # [1,64] knob
|
||||
@export var refresh_max_per_round: int = 2 # [1,8] knob(2026-09-12 轮内刷新批——每轮刷新兜底上限,Active/Waiting 合计)
|
||||
@export var rap_refresh_cost: int = 6 # [1,64] knob(默认值随 AP 压速批 8→6 随动)
|
||||
@export var refresh_cap_per_round: int = 2 # [1,8] knob(2026-09-12 轮内刷新批——每轮刷新兜底上限,Active/Waiting 合计)
|
||||
@export var move_ap_cost: int = 0 # 冻结 0(P4 氛围承载)
|
||||
@export var chain_ap_cost: int = 0 # 冻结 0(v2 定稿)
|
||||
|
||||
@@ -28,8 +28,8 @@ func validate() -> Array:
|
||||
errs.append({"category": "domain", "message": "flag_pull_ap_cost 越域 [0,8]: %d" % flag_pull_ap_cost})
|
||||
if rap_refresh_cost < 1 or rap_refresh_cost > 64:
|
||||
errs.append({"category": "domain", "message": "rap_refresh_cost 越域 [1,64]: %d" % rap_refresh_cost})
|
||||
if refresh_max_per_round < 1 or refresh_max_per_round > 8:
|
||||
errs.append({"category": "domain", "message": "refresh_max_per_round 越域 [1,8]: %d" % refresh_max_per_round})
|
||||
if refresh_cap_per_round < 1 or refresh_cap_per_round > 8:
|
||||
errs.append({"category": "domain", "message": "refresh_cap_per_round 越域 [1,8]: %d" % refresh_cap_per_round})
|
||||
if move_ap_cost != 0:
|
||||
errs.append({"category": "frozen", "message": "move_ap_cost 结构冻结为 0: %d" % move_ap_cost})
|
||||
if chain_ap_cost != 0:
|
||||
|
||||
@@ -9,7 +9,7 @@ dig_ap_cost = 1
|
||||
flag_place_ap_cost = 1
|
||||
flag_pull_ap_cost = 1
|
||||
rap_refresh_cost = 6
|
||||
refresh_max_per_round = 2
|
||||
refresh_cap_per_round = 2
|
||||
move_ap_cost = 0
|
||||
chain_ap_cost = 0
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 实现 GDD:design/ux/battle.md(五区呈现装配器)——读核心只读面(战斗结算 呈现快照 / runner)→
|
||||
# 实现 GDD:design/ux/battle.md(四区呈现装配器)——读核心只读面(战斗结算 呈现快照 / runner)→
|
||||
# 组装文案/投影 → 驱动各视图 setter。
|
||||
# 高内聚边界:"核心状态 → 呈现文案"的全部翻译集中于此;视图保持零核心依赖;
|
||||
# 静态无状态——呈现不持久化任何核心句柄。
|
||||
@@ -9,7 +9,7 @@
|
||||
# 更新触发注记(battle.md Data Requirements):旗/拔/移动成功反馈零事件外发(排雷玩法 发射面封闭)——
|
||||
# 经 ActionOutcome 返回值命令式驱动;AP/RAP 无推送信号——组合根每次调用返回后重查;
|
||||
# 增益状态 纯被动容器零信号——状态栏刷新绑 战斗结算 结算编排完成时点。
|
||||
# Date: 2026-09-12(五区改造批;基础 = 2026-09-11 HUD 装配器;2026-09-13 快照面改造)
|
||||
# Date: 2026-09-12(战斗屏改造批;基础 = 2026-09-11 HUD 装配器;2026-09-13 快照面改造)
|
||||
class_name HudPresenter
|
||||
extends RefCounted
|
||||
|
||||
@@ -68,7 +68,7 @@ static func _refresh_economy(info: HeroInfoPanelView, ap_bar: ApBarView, s: Dict
|
||||
info.set_rap(int(s.rap), int(db.action_economy.rap_refresh_cost))
|
||||
var digging := String(s.phase) == ActionEconomy.PHASE_ACTIVE
|
||||
ap_bar.set_refresh_state(refresh_remaining,
|
||||
bool(s.can_request_refresh) and digging) # Waiting 代理已随 OQ12 退役——UI 仅挖掘期受理
|
||||
bool(s.can_refresh) and digging) # Waiting 代理已随 OQ12 退役——UI 仅挖掘期受理
|
||||
ap_bar.set_stop_enabled(bool(s.hero.get("alive", false)) and digging)
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Validation Question: 线性第一关(战×2→道具→战×2→恢复→Boss)能否承载"数值提升→通关期望改善"的元进度验证
|
||||
# 实现 GDD:design/ux/battle.md(战斗屏 A/B/C/D 四区 + 全屏 States + 遥测;E 区随可选类删除下线 2026-09-15)。
|
||||
# Date: 2026-09-12(battle.md 五区改造批;基础 = 2026-09-11 HUD框架-lite 组合根;code-review 修复批重构)
|
||||
# Date: 2026-09-12(battle.md 战斗屏改造批;基础 = 2026-09-11 HUD框架-lite 组合根;code-review 修复批重构)
|
||||
# 2026-09-13(封装审查修复批):核心状态读取收口至 战斗结算/runner 成文只读面(presentation_snapshot /
|
||||
# current_board / current_battle 等——禁链式穿透 settlement.enemy_system.hero 一类路径);调试模式编排抽离
|
||||
# debug_session.gd(规范 1.1 审视线 + 调试语义单一实现)。
|
||||
@@ -76,19 +76,19 @@ func _ready() -> void:
|
||||
_start_overlay.visible = true
|
||||
|
||||
|
||||
# ==================== 视图接线(battle.md 五区布局 1280×800;节点构建归 UiAssembler) ====================
|
||||
# ==================== 视图接线(battle.md 四区布局 1280×800;节点构建归 UiAssembler) ====================
|
||||
|
||||
func _build_ui() -> void:
|
||||
var root := VBoxContainer.new()
|
||||
add_child(root)
|
||||
var five: Dictionary = UiAssembler.build_five_zone(root, db)
|
||||
strip_view = five.strip_view
|
||||
stage = five.stage
|
||||
log_view = five.log_view
|
||||
info_panel = five.info_panel
|
||||
board_zone = five.board_zone
|
||||
board_view = five.board_view # 探针兼容别名(= board_zone.board_view)
|
||||
ap_bar = five.ap_bar # 探针兼容别名(= board_zone.ap_bar)
|
||||
var battle_ui: Dictionary = UiAssembler.build_battle_ui(root, db)
|
||||
strip_view = battle_ui.strip_view
|
||||
stage = battle_ui.stage
|
||||
log_view = battle_ui.log_view
|
||||
info_panel = battle_ui.info_panel
|
||||
board_zone = battle_ui.board_zone
|
||||
board_view = battle_ui.board_view # 探针兼容别名(= board_zone.board_view)
|
||||
ap_bar = battle_ui.ap_bar # 探针兼容别名(= board_zone.ap_bar)
|
||||
log_view.review_requested.connect(_open_review_from_band)
|
||||
board_zone.action_requested.connect(_on_board_action)
|
||||
board_zone.stop_pressed.connect(_on_stop_pressed)
|
||||
@@ -490,7 +490,7 @@ func _on_stop_pressed() -> void:
|
||||
|
||||
|
||||
func _on_refresh_confirmed() -> void:
|
||||
# 弃图刷新(挖掘期 Active 当场换图继续挖——D4;RAP−8 · 旧板旗作废,每轮上限 2 次)
|
||||
# 弃图刷新(挖掘期 Active 当场换图继续挖——D4;RAP 按门槛扣费 · 旧板旗作废,每轮上限读表)
|
||||
if _input_locked():
|
||||
return
|
||||
var ok: bool = false
|
||||
@@ -502,7 +502,7 @@ func _on_refresh_confirmed() -> void:
|
||||
if ok:
|
||||
_refresh_used_round += 1
|
||||
logger.log_ux("ux_refresh_attempt", {"result": "accepted", "round_refresh_index": _refresh_used_round})
|
||||
board_zone.toast.show_toast("RAP−8 · 旧板旗作废——新图已就位,AP 保留继续挖", "gold")
|
||||
board_zone.toast.show_toast("RAP−%d · 旧板旗作废——新图已就位,AP 保留继续挖" % int(db.action_economy.rap_refresh_cost), "gold")
|
||||
else:
|
||||
# 资源面/上限拒绝(EC13 合法拒绝可见;行动经济 无载荷——客户端按上下文推断)
|
||||
var rap: int = int(settlement.action_economy.rap) if settlement != null else 0
|
||||
@@ -533,7 +533,7 @@ func _handle_outcome_feedback(outcome: Dictionary) -> void:
|
||||
|
||||
func _refresh_presentations() -> void:
|
||||
# 每个核心信号/写调用返回后的全量重查装配(更新触发注记——无推送信号,命令式重查)
|
||||
var refresh_remaining: int = maxi(0, int(db.action_economy.refresh_max_per_round) - _refresh_used_round)
|
||||
var refresh_remaining: int = maxi(0, int(db.action_economy.refresh_cap_per_round) - _refresh_used_round)
|
||||
HudPresenter.refresh(stage, info_panel, ap_bar, settlement, runner, _level_active, db, refresh_remaining)
|
||||
_sync_settlement_visuals()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 实现 GDD:design/ux/battle.md(§Events Fired 遥测义务——ux 行族)。
|
||||
# Date: 2026-09-12(五区改造批增 ux 遥测;基础 = 2026-09-12 日志器首版)
|
||||
# Date: 2026-09-12(战斗屏改造批增 ux 遥测;基础 = 2026-09-12 日志器首版)
|
||||
# 试玩日志器(呈现层组件——订阅核心信号持久化 JSONL,供数值分析;卡死诊断转储)。
|
||||
# 职责边界:核心层只发 settlement_snapshot 信号(战斗结算)/既有信号(battle_finished/node_entered/
|
||||
# level_finished/step_row);本组件负责订阅、缓冲与写文件——核心零日志感知(零 UI 依赖不变)。
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 实现 GDD:design/ux/battle.md(五区装配器——组合根的 UI 构建拆分载体)。
|
||||
# 实现 GDD:design/ux/battle.md(四区装配器——组合根的 UI 构建拆分载体)。
|
||||
# Date: 2026-09-12(code-review 修复批:main.gd 回涨 761 行触规范 1.1 审视线——构建拆出)
|
||||
# 高内聚边界(规范 1.1/1.3):视图构建装配器——只做 new / add_child / 布局,
|
||||
# 零信号连接、零核心写调用;返回句柄供组合根接线(ADR-0001:接线拓扑唯一归组合根)。
|
||||
@@ -9,9 +9,9 @@ extends RefCounted
|
||||
|
||||
# 战斗屏四区(A 进度条 / B 战斗画面 / C·D 两列——E 区道具面板已随可选类删除下线,2026-09-15 格面资源批)。
|
||||
# 返回句柄字典:{strip_view, stage, log_view, info_panel, board_zone, board_view, ap_bar}
|
||||
static func build_five_zone(root: Control, db) -> Dictionary:
|
||||
static func build_battle_ui(root: Control, db) -> Dictionary:
|
||||
# set_anchors_and_offsets_preset(非 set_anchors_preset):后者会把控件重置回最小内容尺寸
|
||||
# (实测 927×613——五区缩顶、棋盘 45px 格 ×10 行超出 D 区 333px 高被裁,2026-09-13 用户报告回归)
|
||||
# (实测 927×613——缩顶、棋盘 45px 格 ×10 行超出 D 区 333px 高被裁,2026-09-13 用户报告回归)
|
||||
root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
root.add_theme_constant_override("separation", 0)
|
||||
|
||||
@@ -41,6 +41,7 @@ static func build_five_zone(root: Control, db) -> Dictionary:
|
||||
|
||||
var board_zone := BoardZoneView.new()
|
||||
bottom.add_child(board_zone)
|
||||
board_zone.ap_bar.set_refresh_cost(int(db.action_economy.rap_refresh_cost)) # RAP 读数表值驱动
|
||||
|
||||
return {
|
||||
"strip_view": strip_view,
|
||||
@@ -89,7 +90,7 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
var title := UiKit.label("Dungeon Sweeper", 34, true)
|
||||
title.add_theme_color_override("font_color", Color8(240, 226, 200))
|
||||
box.add_child(title)
|
||||
var sub := UiKit.label("战斗屏五区改造批 —— 扫雷的因,逐行兑现的果", 16, false)
|
||||
var sub := UiKit.label("战斗屏战斗屏改造批 —— 扫雷的因,逐行兑现的果", 16, false)
|
||||
sub.add_theme_color_override("font_color", Color8(200, 188, 166))
|
||||
box.add_child(sub)
|
||||
box.add_child(HSeparator.new())
|
||||
@@ -113,7 +114,7 @@ static func build_start_overlay(host: Control, db = null) -> Dictionary:
|
||||
|
||||
# 玩法帮助文案(表值驱动——2026-09-16 AP 压速批遗留硬编码 8 随手修正;护甲等级掷点批同步"减伤"→"护甲强化")
|
||||
var help_text := "玩法:每轮 %d AP——挖开(1AP)/插旗(1AP)/移动(免费)/连锁(免费)。\n每个非封锁格都藏一种格面资源(增伤/护甲强化/回血/爆裂/削弱)。开安全格、标中雷 = 正向兑付;\n开雷格、标错安全格 = 负向兑付——全部在轮末结算逐行兑现;吃掷点的基础攻击按护甲等级判定,无 miss、最低保底 1 点。\n停手:剩余 AP 转存 RAP;弃图刷新:RAP−%d 当场换图继续挖(每轮剩 %d 次)。\n敌人全灭胜、主角倒下负。悬停可看三态光标与可挖域微光。" % [
|
||||
int(db.action_economy.ap_per_round), int(db.action_economy.rap_refresh_cost), int(db.action_economy.refresh_max_per_round)]
|
||||
int(db.action_economy.ap_per_round), int(db.action_economy.rap_refresh_cost), int(db.action_economy.refresh_cap_per_round)]
|
||||
var help := UiKit.label(help_text, 13, false)
|
||||
help.add_theme_color_override("font_color", Color8(178, 166, 146))
|
||||
box.add_child(help)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 实现 GDD:design/ux/battle.md(D 区 AP 条 44px——点阵+数字〔pattern 5 点阵资源显示〕、
|
||||
# 停手按钮居左、图例+弃图刷新按钮居右〔雷图右上角〕;弃图刷新确认微交互与常规确认不同位
|
||||
# 〔pattern 7 破坏性确认异位:RAP−8 · 旧板旗作废显式代价〕)。
|
||||
# 〔pattern 7 破坏性确认异位:RAP 扣费 · 旧板旗作废显式代价〕)。
|
||||
# Date: 2026-09-12
|
||||
# 高内聚边界:纯视图——意图经信号外发(stop_pressed/refresh_confirmed/legend_toggled),
|
||||
# 状态由组合根经 setter 注入;刷新可用性预判 = RAP≥8 读数(行动经济 无推送信号——每次调用返回后重查)。
|
||||
@@ -23,6 +23,8 @@ var _btn_legend: Button
|
||||
var _btn_refresh: Button
|
||||
var _refresh_sub: Label
|
||||
var _confirm_row: HBoxContainer
|
||||
var _confirm_lab: Label
|
||||
var refresh_cost: int = 0 # 表值驱动(装配经 set_refresh_cost 注入 ActionEconomyTable.rap_refresh_cost)
|
||||
var _confirm_armed := false
|
||||
var _arm_tween: Tween
|
||||
|
||||
@@ -67,9 +69,9 @@ func _init() -> void:
|
||||
_confirm_row.add_theme_constant_override("separation", 4)
|
||||
_confirm_row.visible = false
|
||||
row.add_child(_confirm_row)
|
||||
var confirm_lab := UiKit.label("RAP−8 · 旧板旗作废", 12, true)
|
||||
confirm_lab.add_theme_color_override("font_color", COL_INK)
|
||||
_confirm_row.add_child(confirm_lab)
|
||||
_confirm_lab = UiKit.label("RAP−%d · 旧板旗作废" % refresh_cost, 14, true)
|
||||
_confirm_lab.add_theme_color_override("font_color", COL_INK)
|
||||
_confirm_row.add_child(_confirm_lab)
|
||||
var confirm_btn := UiKit.button("确认弃图")
|
||||
confirm_btn.pressed.connect(_disarm_and_fire)
|
||||
_confirm_row.add_child(confirm_btn)
|
||||
@@ -87,7 +89,7 @@ func _init() -> void:
|
||||
_btn_refresh = UiKit.button("弃图刷新")
|
||||
_btn_refresh.pressed.connect(_on_refresh_pressed)
|
||||
refresh_box.add_child(_btn_refresh)
|
||||
_refresh_sub = UiKit.label("RAP−8 · 剩 0 次", 10, false)
|
||||
_refresh_sub = UiKit.label("RAP−%d · 剩 0 次" % refresh_cost, 14, false)
|
||||
_refresh_sub.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
refresh_box.add_child(_refresh_sub)
|
||||
|
||||
@@ -107,8 +109,13 @@ func set_ap(ap: int, ap_max: int) -> void:
|
||||
_ap_label.text = "AP %d/%d" % [ap, ap_max]
|
||||
|
||||
|
||||
func set_refresh_cost(cost: int) -> void:
|
||||
refresh_cost = cost
|
||||
_confirm_lab.text = "RAP−%d · 旧板旗作废" % cost
|
||||
|
||||
|
||||
func set_refresh_state(remaining: int, ready: bool) -> void:
|
||||
_refresh_sub.text = "RAP−8 · 剩 %d 次" % remaining
|
||||
_refresh_sub.text = "RAP−%d · 剩 %d 次" % [refresh_cost, remaining]
|
||||
_btn_refresh.disabled = not ready
|
||||
if not ready:
|
||||
_disarm()
|
||||
|
||||
@@ -42,7 +42,7 @@ func _init(db_ref) -> void:
|
||||
_summary.clip_text = true
|
||||
_summary.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
head.add_child(_summary)
|
||||
_hint = UiKit.label("结算明细 ▸ 点击展开回看", 12, false)
|
||||
_hint = UiKit.label("结算明细 ▸ 点击展开回看", 14, false)
|
||||
_hint.size_flags_horizontal = Control.SIZE_SHRINK_END
|
||||
head.add_child(_hint)
|
||||
_log = RichTextLabel.new()
|
||||
|
||||
@@ -19,6 +19,8 @@ const PENDING_STYLE := {
|
||||
"damage": ["⚔", Color8(198, 61, 34)],
|
||||
"buff": ["✦", Color8(46, 110, 142)],
|
||||
}
|
||||
# 蓄力档技能集(2026-09-18 用户拍板:重击类独立蓄力提示——伤害技 ⚔ / 增益技 ✦ / 重击类 蓄力 三档占位)
|
||||
const CHARGE_SKILL_IDS := ["heavy"]
|
||||
# 护甲类型呈现(攻击类型×护甲克制批——可读性前提:玩家须能看出该用穿刺还是魔法打它)
|
||||
const ARMOR_NAMES := {"light": "轻甲", "ward": "魔法护甲", "heavy": "重甲"}
|
||||
|
||||
@@ -82,7 +84,7 @@ func set_data(d: Dictionary) -> void:
|
||||
_set_order(int(d.get("order", 1)))
|
||||
_armor = String(d.get("armor_type", "light"))
|
||||
_armor_class = int(d.get("armor_class", 0))
|
||||
set_pending(String(d.get("pending_type", "")), String(d.get("pending_name", "")))
|
||||
set_pending(String(d.get("pending_type", "")), String(d.get("pending_skill", "")))
|
||||
# 状态行:伏击池(flat 共享池,单独通道)+ 敌方增益 percent 状态(蓄力/硬化/削弱/暴走——side-wide)
|
||||
var parts: Array = []
|
||||
var ambush: int = int(d.get("ambush_layers", 0))
|
||||
@@ -105,15 +107,17 @@ func _set_order(i: int) -> void:
|
||||
# 摇定动作渲染(敌人技能池批——presenter 不翻译,渲染映射唯一定义处归本类〔code-review 2026-09-12 先例〕):
|
||||
# pending_type = 战斗结算 快照投影(basic/damage/buff;"" = 未摇定/死亡残位)。
|
||||
# 信号/入参契约:set_data 每结算行刷新、set_pending 每轮开轮刷新(开轮摇号时点)。
|
||||
func set_pending(ptype: String, pname: String) -> void:
|
||||
func set_pending(ptype: String, pid: String) -> void:
|
||||
if _dead:
|
||||
return
|
||||
var style: Array = PENDING_STYLE.get(ptype, ["—", Color8(120, 106, 88)])
|
||||
_intent_badge.add_theme_color_override("font_color", style[1])
|
||||
if ptype == "" or ptype == "basic":
|
||||
_intent_badge.text = "意图:—" # 普通攻击零表示(不可预测——§2.4)
|
||||
elif pid in CHARGE_SKILL_IDS:
|
||||
_intent_badge.text = "意图:蓄力" # 重击类蓄力档(2026-09-18 用户三档拍板占位;终稿动画归战斗表现样张批)
|
||||
else:
|
||||
_intent_badge.text = "意图:%s %s" % [String(style[0]), pname] # 特殊技类型级占位(纯提醒,不可撤销)
|
||||
_intent_badge.text = "意图:%s" % String(style[0]) # 特殊技类型级占位——不预告技能名与数值(HUD框架;2026-09-18 剧透堵口)
|
||||
# 护甲行(环点拆除——护甲类型 + AC 恒常驻,克制对策与掷点阈值的可读前提)
|
||||
_armor_line.text = "%s · AC %d" % [_armor_tag(_armor), _armor_class]
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 实现 GDD:design/ux/battle.md(A 区关卡进度条——全宽 × 28px 顶栏,当前节点高亮、已过节点暗金)。
|
||||
# Date: 2026-09-12(battle.md 五区改造批——对齐 28px 顶栏与高亮样式;基础 = 2026-09-11 关卡节点地图-lite 呈现)
|
||||
# Date: 2026-09-12(battle.md 战斗屏改造批——对齐 28px 顶栏与高亮样式;基础 = 2026-09-11 关卡节点地图-lite 呈现)
|
||||
# 2026-09-17 分支节点图批:图形态行集渲染 = 行进序列(已到暗金 → 当前金 → 可选下一跳亮边候选);
|
||||
# 单链形态(第一关零迁移)保持全链横条渲染。
|
||||
# 高内聚边界:只读 runner 的节点链/进度/候选做渲染,无任何写面。
|
||||
|
||||
@@ -47,7 +47,7 @@ static func perfect_action(minesweeper, settlement) -> Dictionary:
|
||||
for c in ded["safe"]:
|
||||
if not minesweeper.is_revealed(c) and not minesweeper.has_flag(c):
|
||||
return _plan_on(minesweeper, c, "dig")
|
||||
if settlement != null and bool(settlement.can_request_refresh()):
|
||||
if settlement != null and bool(settlement.can_refresh()):
|
||||
return {"kind": "refresh"}
|
||||
return {"kind": "stop"}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 验证问题:运行期数据致命通道是否成立(2026-09-18 用户拍板——进度坐标越域记 Fatal 级日志后进程退出,退出码 1)
|
||||
# 运行:Godot_console.exe --headless --path . --script res://smoke/config_fatal_probe.gd
|
||||
# 预期:进程以退出码 1 终止;打印 FAIL 行或退出码 0 即致命通道未生效。
|
||||
extends SceneTree
|
||||
|
||||
var _frames := 0
|
||||
|
||||
func _initialize() -> void:
|
||||
var db = load("res://core/config_db.gd").new()
|
||||
root.add_child(db) # 入树——autoload 同款生命周期
|
||||
db.load_tables("res://data/tables", false)
|
||||
db.resolve_enemy_rowset(0, 0) # 越域:关卡序号 <1 → Fatal,quit(1) 排队帧末退出
|
||||
print("FATAL-PROBE — Fatal 已记,等待帧末退出(退出码应为 1)")
|
||||
|
||||
func _process(_delta: float) -> bool:
|
||||
_frames += 1
|
||||
if _frames > 5:
|
||||
print("FATAL-PROBE FAIL — 5 帧内未退出,致命通道未生效")
|
||||
quit(0)
|
||||
return true
|
||||
return false
|
||||
@@ -409,6 +409,10 @@ func _smoke_board_generator() -> void:
|
||||
for L in [1, 2]:
|
||||
var row = _db.resolve_board_gen(L, 0)
|
||||
check(not row.is_empty(), "BoardGen 行解析 (%d,0)" % L)
|
||||
if L == 2:
|
||||
# R1 ceil 口径(2026-09-18 拍板):无精确行向上取整保证偏难——L2 深度 3 为恢复点无行,解析到深度 4
|
||||
var ceil_row: Dictionary = _db.resolve_board_gen(2, 3)
|
||||
check(int(ceil_row.get("node_depth", -1)) == 4, "R1 ceil 解析 (2,3)→(2,4)(向上取整偏难)")
|
||||
var b1 = board_generator.generate(L, 0, _rng.open_stream("board_gen"), _db)
|
||||
check(b1 != null, "L%d 生成产物非空" % L)
|
||||
if b1 == null:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 实现 GDD:design/ux/battle.md(§AC 场景级测试 + §Interaction Map——UI 行为随机化探针 v3)。
|
||||
# Date: 2026-09-12(五区改造批扩展;基础 = 2026-09-11 问题3 复现器 v2——既有断言全保留)
|
||||
# Date: 2026-09-12(战斗屏改造批扩展;基础 = 2026-09-11 问题3 复现器 v2——既有断言全保留)
|
||||
# 合成鼠标事件走真实呈现层路径(BoardView.click_cell / 按钮信号 → 组合根动作通道),
|
||||
# 随机混合玩家行为——插旗/拔旗、提前停手、弃图刷新确认链、图例模态、明细回看模态、
|
||||
# tooltip 悬停、无效点击——扫描结算停滞(缺陷②回归载体;缺陷④窗口契约随道具窗口删除退役 2026-09-15)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# Date: 2026-09-13(五区布局诊断探针——"内容未满幅/棋盘底裁切"回归定位:打印窗口/视口/五区实际矩形
|
||||
# Date: 2026-09-13(四区布局诊断探针——"内容未满幅/棋盘底裁切"回归定位:打印窗口/视口/四区实际矩形
|
||||
# 与最小尺寸、展开旗标,对照 battle.md §Layout 1280×800 基座规格)
|
||||
# 运行:Godot_console.exe --headless --path . res://smoke/ui_layout_probe.tscn
|
||||
extends Node
|
||||
@@ -33,7 +33,7 @@ func _ready() -> void:
|
||||
print("%-16s pos=%s size=%s min=%s hflag=%d vflag=%d" % [k, ctl.position, ctl.size,
|
||||
ctl.custom_minimum_size, ctl.size_flags_horizontal, ctl.size_flags_vertical])
|
||||
var root_box: Control = main.get_child(0)
|
||||
print("five-zone root: anchors=(%s,%s)-(%s,%s) offsets=(%s,%s,%s,%s) size=%s" % [
|
||||
print("four-zone root: anchors=(%s,%s)-(%s,%s) offsets=(%s,%s,%s,%s) size=%s" % [
|
||||
root_box.anchor_left, root_box.anchor_top, root_box.anchor_right, root_box.anchor_bottom,
|
||||
root_box.offset_left, root_box.offset_top, root_box.offset_right, root_box.offset_bottom, root_box.size])
|
||||
print("board_view cell_size=%s board=%s" % [main.board_view.get("cell_size"), main.board_view.board()])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||||
# 实现 GDD:design/ux/battle.md(场景级 headless 探针——五区布局实例化断言;entry 17②③)。
|
||||
# Date: 2026-09-12(五区改造批扩展;基础 = 2026-09-11 首战棋盘探针——既有断言全保留)
|
||||
# 实现 GDD:design/ux/battle.md(场景级 headless 探针——四区布局实例化断言;entry 17②③)。
|
||||
# Date: 2026-09-12(战斗屏改造批扩展;基础 = 2026-09-11 首战棋盘探针——既有断言全保留)
|
||||
# 项目运行模式(autoload 在位)实例化 main.tscn,直接驱动 _start_full_level(),
|
||||
# 断言四区真实构建(棋盘/进度条/英雄卡/敌人卡/AP 点阵/明细带/数据框;E 区道具面板已删除)。
|
||||
# 运行:Godot_console.exe --headless --path . res://smoke/ui_probe.tscn
|
||||
@@ -29,7 +29,7 @@ func _ready() -> void:
|
||||
problems.append("首战未处于进行中")
|
||||
if not main.log_view.parsed_text().contains("节点 1/"):
|
||||
problems.append("节点头日志缺失")
|
||||
# ---- 五区布局断言(battle.md §Layout)----
|
||||
# ---- 四区布局断言(battle.md §Layout)----
|
||||
if not main.stage.round_label.text.contains("第 1 轮"):
|
||||
problems.append("B 区轮次·阶段指示缺失(%s)" % main.stage.round_label.text)
|
||||
if main.stage.enemy_box.get_child_count() < 1:
|
||||
|
||||
@@ -13,6 +13,11 @@ func _ready() -> void:
|
||||
add_child(main)
|
||||
await get_tree().process_frame
|
||||
var problems: Array = []
|
||||
# 基线读表(2026-09-18 随数值压缩批改表值驱动——旧字面量 6/50、12/75 已随 hero 表压缩失效)
|
||||
var base_atk := int(main.db.hero.atk)
|
||||
var base_hp := int(main.db.hero.max_hp)
|
||||
var boosted_atk := base_atk * 2 # 攻击滑条 100 → ×2
|
||||
var boosted_hp := int(round(base_hp * 1.5)) # 生命滑条 50 → +50%
|
||||
# ① 打开可见性(缺陷⑤回归:打开面板必须隐藏不透明的开始覆盖层)
|
||||
main._show_tune_panel()
|
||||
if not main.tune_panel.visible:
|
||||
@@ -21,7 +26,7 @@ func _ready() -> void:
|
||||
problems.append("开始覆盖层未隐藏——面板被遮挡(缺陷⑤复发)")
|
||||
# ② 滑条 → 生效预览联动
|
||||
main.tune_panel.set_sliders(100, 50)
|
||||
if "12" not in main.tune_panel.effect_text() or "75" not in main.tune_panel.effect_text():
|
||||
if str(boosted_atk) not in main.tune_panel.effect_text() or str(boosted_hp) not in main.tune_panel.effect_text():
|
||||
problems.append("生效预览未联动(%s)" % main.tune_panel.effect_text())
|
||||
# ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照
|
||||
main.tune_panel.press_apply_for_probe()
|
||||
@@ -50,19 +55,19 @@ func _ready() -> void:
|
||||
if main.tune_panel.visible or not main._start_overlay.visible:
|
||||
problems.append("关闭后层级状态错误")
|
||||
# ⑦ hero 表运行期不可变(E4 收口回归断言:任何代码 reintroduce 表写在此被抓获——原"表值残留"断言语义升级)
|
||||
if int(main.db.hero.atk) != 6 or int(main.db.hero.max_hp) != 50:
|
||||
problems.append("hero 表被运行时改写(E4 违例:atk=%s hp=%s,期望恒基线 6/50)" % [str(main.db.hero.atk), str(main.db.hero.max_hp)])
|
||||
if int(main.db.hero.atk) != base_atk or int(main.db.hero.max_hp) != base_hp:
|
||||
problems.append("hero 表被运行时改写(E4 违例:atk=%s hp=%s,期望恒基线 %d/%d)" % [str(main.db.hero.atk), str(main.db.hero.max_hp), base_atk, base_hp])
|
||||
# ⑧ 单次加成生效回归断言:应用加成 + 启动单场战斗 → 呈现快照 hero 必须是"基线 × 单次加成"
|
||||
# (6×2=12 / 50×1.5=75)——旧缺陷(残留值被当基线再乘 → 24/112)在 encounter 参数链上同样被抓获
|
||||
# (攻击 ×2 / 生命 +50%)——旧缺陷(残留值被当基线再乘)在 encounter 参数链上同样被抓获
|
||||
main.tune_panel.set_sliders(100, 50)
|
||||
main.tune_panel.press_apply_for_probe()
|
||||
main._start_full_level() # 完整关卡路径(_start_single 按语义清零滑条 = 恒基线,不承载加成断言)
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
var hero: Dictionary = main.settlement.presentation_snapshot().hero
|
||||
if int(hero.get("atk", -1)) != 12 or int(hero.get("max_hp", -1)) != 75:
|
||||
problems.append("战斗加成单次生效违例(atk=%s max_hp=%s,期望 12/75)" % [
|
||||
str(hero.get("atk", "?")), str(hero.get("max_hp", "?"))])
|
||||
if int(hero.get("atk", -1)) != boosted_atk or int(hero.get("max_hp", -1)) != boosted_hp:
|
||||
problems.append("战斗加成单次生效违例(atk=%s max_hp=%s,期望 %d/%d)" % [
|
||||
str(hero.get("atk", "?")), str(hero.get("max_hp", "?")), boosted_atk, boosted_hp])
|
||||
if problems.is_empty():
|
||||
print("[tune_probe] PASS — 打开/联动/应用/重置/模拟/关闭/基线无残留/单次加成 全链路通过(模拟 N=10 输出 %.30s…)" % out)
|
||||
get_tree().quit(0)
|
||||
|
||||
Reference in new issue
Block a user