merge: 迭代26 UI交互与观感批全量入库(七提交, worktree g-plan2-iter26-ui)

This commit is contained in:
DKChan committed 2026-10-14 06:34:41 +08:00
commit c8c3db1052
17 files changed
+442 -48

No files matched your search

+6 -6
View File
@@ -330,19 +330,18 @@ func _build_ui() -> void:
_inter_label = inter.label
inter.next_btn.pressed.connect(_on_inter_next_pressed)
var diag_btn := UiAssembler.build_diag_button(self)
diag_btn.pressed.connect(_on_diagnose_pressed)
# 确认弹窗(二轮修改批 A——新冒险互斥确认弹窗化):树序落在开始层/过场层/诊断钮之后 = 子级最末
# 确认弹窗(二轮修改批 A——新冒险互斥确认弹窗化):树序落在开始层/过场层之后 = 子级最末
#(T3 收口批——「登记序 = 树序 = z 序」纪律复位,confirm_dialog.open 的 move_child 运行时自提
# 创可贴随之删除;弹窗只在开始菜单域弹出,绘制序恒最上)
# 创可贴随之删除;弹窗只在开始菜单域弹出,绘制序恒最上。原诊断转储角落钮随迭代 26 T5b 退役,
# 转储入口迁调试浮窗——grilling Q8 裁甲)
confirm_dialog = ConfirmDialog.new()
add_child(confirm_dialog)
confirm_dialog.confirmed.connect(_on_confirm_new_adventure)
confirm_dialog.cancelled.connect(_on_confirm_cancelled)
# 调试浮窗(迭代 14.5 调试整合批——主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口;
# 迭代 18 卡⑦——升级为全局可开浮窗:DS_DEBUG=1 启动即开〔_ready〕+两屏角标再开〔P2 甲〕+当局数据只读区块〔T3〕)。
# 迭代 18 卡⑦——升级为全局可开浮窗:DS_DEBUG=1 启动即开〔_ready〕+两屏角标再开〔P2 甲〕+当局数据只读区块〔T3〕;
# 迭代 26 T5b——诊断转储入口并入(原主画面左下角常驻钮退役,玩家画面清零))。
# 树序在开始层与确认弹窗之后 = 浮于菜单之上的 Dialog(不隐藏开始层——与确认弹窗同款形态);
# 开窗唯一入口 = _show_debug_overlay(C6 单口——角标/探针/启动路径全收口)
debug_overlay = DebugOverlay.new(db, int(db.hero.atk), int(db.hero.max_hp))
@@ -352,6 +351,7 @@ func _build_ui() -> void:
debug_overlay.apply_requested.connect(_on_tune_apply)
debug_overlay.simulate_requested.connect(_on_tune_simulate)
debug_overlay.debug_levels_requested.connect(_start_debug_levels)
debug_overlay.diagnose_requested.connect(_on_diagnose_pressed) # 迭代 26 T5b——诊断转储入口随迁(原角落钮连接)
# 模态登记(缺陷⑤纪律——登记序 = 树序:含 move_child 调位后的关卡画面/三选一终位;
# 状态面板设置层同档、确认弹窗之前——P2 甲;生涯页同档随状态面板之后(迭代 18 卡④ P3 甲))
-12
View File
@@ -216,18 +216,6 @@ static func build_inter_overlay(host: Control) -> Dictionary:
return {"overlay": overlay, "label": label, "next_btn": next_btn}
# Playtest 诊断按钮(调试变体——battle.md §States 允许;卡死转储)
static func build_diag_button(host: Control) -> Button:
var diag_btn := UiKit.button("诊断转储", 0.0) # 角落小件豁免宽度基准(固定 offsets 112px 宽)
diag_btn.set_anchors_preset(Control.PRESET_BOTTOM_LEFT)
diag_btn.offset_left = 6.0
diag_btn.offset_top = -30.0
diag_btn.offset_right = 118.0
diag_btn.offset_bottom = -6.0
host.add_child(diag_btn)
return diag_btn
static func overlay_bg(host: Control, color: Color) -> ColorRect:
var dim := ColorRect.new()
dim.color = color
+1 -1
View File
@@ -74,7 +74,7 @@ func _init() -> void:
_rap_label = UiKit.label("RAP 0",UiKit.FS_HINT, true, UiKit.INK_LABEL) # 显式传色(UI 设计对齐批 N1-7——T0 实测对 PANEL_DATA 10.4:1 已合规,显式化仅为确定性加固,值不变)
_rap_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER
row.add_child(_rap_label)
_rap_feedback = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_FAINT)
_rap_feedback = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP) # 迭代 26 T3 换墨——旧灰石弱化墨对 PANEL_DATA 2.9:1 越带,深石墨 5.6:1 在案
_rap_feedback.size_flags_vertical = Control.SIZE_SHRINK_CENTER
row.add_child(_rap_feedback)
@@ -118,10 +118,12 @@ func _init() -> void:
tree_scroll.add_child(_tree_holder)
var tree_panel := _counter_panel("天赋树 · 淬炼", "", tree_scroll, 300)
tree_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
tree_panel.size_flags_vertical = Control.SIZE_FILL # 迭代 26 T5③ 四栏等高:任一栏长高时四栏同步拉伸
mid.add_child(tree_panel)
# 柜二·锻造工坊(钥匙门——forge_workshop 点亮前锁定态;点亮=工作间两区六路线行板,迭代 23 T6 实装)
_forge_lock_l = UiKit.label("🔒 锻造工坊未开放\n\n点亮树上「锻造工坊」钥匙节点后展开工作间。\n(钥匙免费、无前置——树上直接点亮)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
_forge_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 迭代 26 T5③ 锁文在等高栏内垂直居中
_forge_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_forge_lock_l.custom_minimum_size = Vector2(280, 0)
_forge_workbench = VBoxContainer.new()
@@ -139,14 +141,17 @@ func _init() -> void:
_forge_workbench.add_child(_forge_zone_armor)
var forge_holder := VBoxContainer.new()
forge_holder.add_theme_constant_override("separation", 6)
forge_holder.custom_minimum_size = Vector2(0, 336) # 迭代 26 T5③ 四栏等高(与树/秘宝匣 scroll 336 min 对齐——锁定态仅锁文也撑住栏高)
forge_holder.add_child(_forge_lock_l)
forge_holder.add_child(_forge_workbench)
var forge_panel := _counter_panel("锻造工坊", UiKit.TEX_ICON_FORGE, forge_holder, 300)
forge_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
forge_panel.size_flags_vertical = Control.SIZE_FILL # T5③ 四栏等高:任一栏长高时四栏同步拉伸
mid.add_child(forge_panel)
# 柜三·秘宝匣(钥匙门——ks_workshop 点亮前锁定态;点亮=现工坊内容整体迁入:六行+同调装配段+Y 升级行)
_ws_lock_l = UiKit.label("🔒 秘宝匣未开放\n\n点亮树上「秘宝匣」钥匙节点后展开。\n(前置=脊柱生命第 1 级)",UiKit.FS_HINT, false, UiKit.STONE_DEEP)
_ws_lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 迭代 26 T5③ 锁文在等高栏内垂直居中
_ws_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
_ws_lock_l.custom_minimum_size = Vector2(280, 0)
_ws_scroll = ScrollContainer.new()
@@ -158,18 +163,23 @@ func _init() -> void:
_ws_scroll.add_child(_workshop_holder)
var ws_holder := VBoxContainer.new()
ws_holder.add_theme_constant_override("separation", 6)
ws_holder.custom_minimum_size = Vector2(0, 336) # 迭代 26 T5③ 四栏等高(锁定态 scroll 隐、仅锁文也撑住栏高)
ws_holder.add_child(_ws_lock_l)
ws_holder.add_child(_ws_scroll)
var ws_panel := _counter_panel("秘宝匣", UiKit.TEX_ICON_WORKSHOP, ws_holder, 300)
ws_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
ws_panel.size_flags_vertical = Control.SIZE_FILL # T5③ 四栏等高:任一栏长高时四栏同步拉伸
mid.add_child(ws_panel)
# 柜四·奖励格图鉴(常开独立位——结构位预留,内容排 Alpha)
var atlas_holder := VBoxContainer.new()
atlas_holder.custom_minimum_size = Vector2(0, 336) # 迭代 26 T5③ 四栏等高(图鉴仅锁文也撑住栏高)
var atlas_panel := _counter_panel("奖励格图鉴", UiKit.TEX_ICON_ATLAS, atlas_holder, 300)
atlas_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
atlas_panel.size_flags_vertical = Control.SIZE_FILL # T5③ 四栏等高:任一栏长高时四栏同步拉伸
mid.add_child(atlas_panel)
var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE)
lock_l.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 迭代 26 T5③ 锁文在等高栏内垂直居中
lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
lock_l.custom_minimum_size = Vector2(280, 0)
atlas_holder.add_child(lock_l)
+2 -1
View File
@@ -40,7 +40,8 @@ func open(text: String, confirm_label: String, cancel_label: String = "取消")
_confirm_btn.text = confirm_label
_cancel_btn.text = cancel_label
# z 序纪律(T3 收口批 2026-10-02):树序由组合根构建序保证——本弹窗在 main._build_ui 尾部
# add_child(开始层/过场层/诊断钮之后 = 子级最末,登记序 = 树序 = 绘制序恒最上),
# add_child(开始层/过场层之后 = 子级最末,登记序 = 树序 = 绘制序恒最上;原诊断转储角落钮随
# 迭代 26 T5b 退役迁调试浮窗,此处不再经其之后),
# 「每次打开 move_child 运行时自提」创可贴随之删除。
visible = true
+17
View File
@@ -16,6 +16,7 @@ extends Control
signal apply_requested(atk_pct: int, hp_pct: int) # 应用并关闭——加成落表归组合根单一应用路径
signal simulate_requested(count: int) # 批量模拟编排归组合根(表基线复位 + 单路径施加)
signal debug_levels_requested # 两层调试对局入口(编排归 debug_session.gd)
signal diagnose_requested # 诊断转储入口(迭代 26 T5b——原主画面角落钮迁入;卡死取证编排归组合根)
signal closed
var seed_edit: LineEdit # 组合根 _seed_value 唯一读取面(原开始覆盖层种子行迁入)
@@ -265,6 +266,18 @@ func _build() -> void:
_dbg_btn.pressed.connect(_emit_debug_levels)
list.add_child(_dbg_btn)
# 区块⑤诊断转储(迭代 26 T5b——原主画面左下角「诊断转储」常驻钮退役迁入:grilling Q8 裁甲,
# 承迭代 18 卡⑦「主菜单三散件收一」纪律;dump_diagnosis 卡死取证功能保留〔删除案否〕,
# 门控隐藏案否——本浮窗本就可全局可开,双轨无意义。纯视图边界:转储编排经信号外发归组合根)
var diag_row := HBoxContainer.new()
diag_row.add_theme_constant_override("separation", 10)
list.add_child(diag_row)
diag_row.add_child(UiKit.label("诊断转储",UiKit.FS_BODY, false))
var diag_btn := UiKit.button("转储")
diag_btn.tooltip_text = "卡死取证:全量核心状态+环形缓冲 → user://logs/diag_*.json"
diag_btn.pressed.connect(_emit_diagnose_requested)
diag_row.add_child(diag_btn)
var close_btn := UiKit.button("关闭")
close_btn.pressed.connect(_emit_closed)
box.add_child(close_btn)
@@ -298,6 +311,10 @@ func _emit_simulate_requested() -> void:
simulate_requested.emit(int(_sim_count.value))
func _emit_diagnose_requested() -> void:
diagnose_requested.emit()
func _emit_debug_levels() -> void:
debug_levels_requested.emit()
@@ -63,6 +63,7 @@ func _init() -> void:
box.add_child(_visit_label)
_cards_box = VBoxContainer.new()
_cards_box.size_flags_vertical = Control.SIZE_EXPAND_FILL
_cards_box.alignment = BoxContainer.ALIGNMENT_CENTER # 迭代 26 T5③ 卡片区垂直居中(旧默认顶对齐=卡片堆上半、下半空)
_cards_box.add_theme_constant_override("separation", 8)
box.add_child(_cards_box)
var foot := HBoxContainer.new()
+4 -4
View File
@@ -433,12 +433,12 @@ func _draw() -> void:
draw_rect(ped, UiKit.STAND_STONE_EDGE, false, 1.0)
draw_rect(Rect2(ped.position.x, ped.position.y, ped.size.x, 2.0), UiKit.STAND_STONE_RIM, true)
# 血条(直角递减):敌附顺位徽标(GOLD_DEEP ①②③)——旧 top VBox 几何等价(左 8 / 右 -8)
# 血条(直角递减):敌附顺位徽标(GOLD_BRIGHT ①②③——迭代 26 T3 换墨,旧 GOLD_DEEP 对舞台底 3.3:1 越带)——旧 top VBox 几何等价(左 8 / 右 -8)
var bar_x := 8.0
if _kind == "enemy":
var order_txt := String("①②③"[clampi(_order - 1, 0, 2)])
f.draw_string(get_canvas_item(), Vector2(bar_x, 12), order_txt,
HORIZONTAL_ALIGNMENT_LEFT, -1, 13, UiKit.GOLD_DEEP)
HORIZONTAL_ALIGNMENT_LEFT, -1, 13, UiKit.GOLD_BRIGHT)
bar_x += 20.0
var bar_w := box.x - 8.0 - bar_x
draw_rect(Rect2(bar_x, 2, bar_w, 10), Color(0.10, 0.10, 0.11)) # 条底(引擎默认主题深底近似平移)
@@ -489,9 +489,9 @@ func _draw() -> void:
var col: Color = UiKit.WARM_FAINT # 轻甲 = 暖白弱化(v3.5 暗底双色墨制——原灰石对暗底不可读)
match _armor:
"ward":
col = UiKit.HERO_BLUE # 魔法护甲 = 小人蓝
col = UiKit.HERO_BLUE_BRIGHT # 魔法护甲 = 小人蓝亮档(迭代 26 T3 换墨,旧 HERO_BLUE 对舞台底 3.2:1 越带)
"heavy":
col = UiKit.GOLD_DEEP # 重甲 = 深金
col = UiKit.GOLD_BRIGHT # 重甲 = 金族亮档(迭代 26 T3 换墨,旧 GOLD_DEEP 对舞台底 3.3:1 越带)
var name_w := f.get_string_size(_display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, 12).x
var armor_w := f.get_string_size(armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, 12).x
var total_w := armor_w + 3.0 + name_w
+56 -8
View File
@@ -27,6 +27,7 @@ const PANEL_BORDER := Color8(120, 110, 88) # 面板细线框;兼熄灭点
const GOLD := Color8(240, 168, 40) # 奖励金
const GOLD_DEEP := Color8(176, 122, 20) # 深金(金框描边/达意金)
const GOLD_INK := Color8(116, 80, 11) # 深金墨(意图蓄力徽标墨——PANEL_DATA 垫牌上 4.9:1,UI 设计对齐批 N1 2026-10-06)
const GOLD_BRIGHT := Color8(224, 184, 96) # 金族亮档(迭代 26 T3——敌顺位徽标①②③/重甲徽标对暗舞台底 6.5:1;值=收益金 e0b860 同门;GOLD_DEEP 语义岗〔standee:199 意图判据/board_view:35/level_map:237〕在役不动)
const GOLD_LIT := Color8(230, 190, 90) # 亮金(候选高亮)
const GOLD_DIM := Color8(140, 116, 60) # 暗金(已过节点)
const GOLD_DONE := Color8(104, 86, 44) # 已过节点暗金·关卡画面呈现态(关卡画面样张 v1 冻结新值 #68562C——旧 GOLD_DIM #8C743C 对双色墨均 <4.5:1 对比硬指标逼守;GOLD_DIM 他用不动)
@@ -41,6 +42,7 @@ const RED_ALERT := Color8(200, 60, 48) # 报错朱
const HERO_BLUE := Color8(46, 134, 222) # 小人蓝 #2E86DE(样张 v2 定稿 2026-09-28——圣经 §4.1 候选值冻结;旧 #2E6E8E 收敛期快照退役)
const HERO_BLUE_DEEP := Color8(30, 90, 150) # 小人蓝同族深档(意图增益徽标墨——PANEL_DATA 垫牌上 4.8:1;命名同 CONTACT_GLOW_DEEP 规则,UI 设计对齐批 N1 2026-10-06)
const HERO_BLUE_BRIGHT := Color8(126, 186, 240) # 小人蓝同族亮档(迭代 26 T3——魔法护甲徽标对暗舞台底 5.9:1;HERO_BLUE 语义岗〔board_view:35 小人边框/level_map:237〕在役不动)
const BOSS_PURPLE := Color8(143, 47, 106) # 紫赤(Boss 专属)
const DEBUFF_PURPLE := Color8(122, 66, 148) # 妨害紫(意图 ☠)
@@ -104,6 +106,7 @@ const DIGIT_PLATE := Color8(245, 234, 211) # 数字色牌(素色暖纸白
# 可读不依赖格底——原则二;2026-09-26 呈现修复批落码)
const DIGIT_PLATE_EDGE := Color8(210, 198, 172) # 色牌牌缘线
const SLOT_BACKDROP := Color8(62, 54, 44) # B 区状态图标槽实底(深墨棕——白图标对比承载)#3E362C(P7 乙 2026-10-06 对齐)
const SLOT_BACKDROP_SOFT := Color8(128, 106, 78) # 图标槽实底·柔和档(迭代 26 T5②——BCard 图标槽明度差降档:对米色卡面 3.7:1〔旧深档 8.7:1 过硬〕,白图标 5.1:1 ≥3:1 图形对比硬约束;B 区状态槽仍用深档 SLOT_BACKDROP)
const BG_ROOT := Color8(58, 46, 34) # 屏幕根底 #3A2E22(UI 落码批 M0 2026-10-04——配色总纲「提暖提亮禁近黑」,旧近黑 #1E1914 退役)
const BG_OVERLAY := Color8(54, 43, 32) # 模态遮罩底 #362B20(同批换代,旧 #1C1612 退役)
@@ -253,25 +256,37 @@ static func vignette_rect() -> TextureRect:
var tr := TextureRect.new()
tr.texture = t
tr.stretch_mode = TextureRect.STRETCH_SCALE
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(迭代 26 T2——全局 Nearest 下显式豁免)
tr.set_anchors_preset(Control.PRESET_FULL_RECT)
tr.mouse_filter = Control.MOUSE_FILTER_IGNORE
return tr
# 斜光带(demo #v-start .beams——repeating-linear-gradient 102deg,暖白 5% 窄带 240-340/560px;
# 程序纹理循环带,确定性生成零随机)
# 程序纹理循环带,确定性生成零随机。迭代 26 T4 光柱硬边修(grilling Q7 裁=连续 alpha):
# 带内 alpha 0→0.05→0 横向过渡(旧匀净+零过渡硬边梯形退役)+纵向衰减上亮下暗;
# 纹理 filter=显式 LINEAR 白名单(与暗角/地图渐变同血统,见 T2)
static func beams_rect() -> TextureRect:
var img := Image.create(560, 560, false, Image.FORMAT_RGBA8)
var ca := cos(0.2094) # 12°(102deg 带轴的垂直倾角)
var sa := sin(0.2094)
var beam_col := Color(1.0, 236.0 / 255.0, 200.0 / 255.0)
var band_lo := 240.0
var band_hi := 340.0
var band_mid := (band_lo + band_hi) * 0.5
var band_peak := 0.05
var band_half := (band_hi - band_lo) * 0.5
for y in 560:
var v_fall := 1.0 - float(y) / 560.0 # 纵向衰减:上亮下暗(连续 alpha)
for x in 560:
var d := fposmod(x * ca + y * sa, 560.0)
if d >= 240.0 and d < 340.0:
img.set_pixel(x, y, Color(1.0, 236.0 / 255.0, 200.0 / 255.0, 0.05))
if d >= band_lo and d < band_hi:
var a: float = band_peak * (1.0 - absf(d - band_mid) / band_half) * v_fall
img.set_pixel(x, y, Color(beam_col.r, beam_col.g, beam_col.b, a))
var tr := TextureRect.new()
tr.texture = ImageTexture.create_from_image(img)
tr.stretch_mode = TextureRect.STRETCH_TILE
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(迭代 26 T2 Q7 裁——连续 alpha 与暗角/地图渐变同血统)
tr.set_anchors_preset(Control.PRESET_FULL_RECT)
tr.mouse_filter = Control.MOUSE_FILTER_IGNORE
return tr
@@ -291,6 +306,7 @@ static func gradient_base(top: Color, low: Color, stop := 0.4) -> TextureRect:
var tr := TextureRect.new()
tr.texture = t
tr.stretch_mode = TextureRect.STRETCH_SCALE
tr.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR # 平滑渐变件白名单(迭代 26 T2——全局 Nearest 下显式豁免)
tr.set_anchors_preset(Control.PRESET_FULL_RECT)
tr.mouse_filter = Control.MOUSE_FILTER_IGNORE
return tr
@@ -537,22 +553,43 @@ static func keystone_badge(text: String, icon_path := TEX_ICON_KEYSTONE, icon_px
return KeystoneBadge.new(text, icon_path, icon_px)
# 按钮皮(常态/按压两态+墨字——缺位 = 引擎默认主题,素材批前逐位一致)
# 按钮皮·五态矩阵(迭代 26 T1——UI 交互与观感批;甲/乙案值=grilling 两轮八裁落文,逐实例 override 不动主题层)
# 悬停=乙案:常态图+modulate 提亮(0.06 档与 primary_button 现行 lightened(0.06) 同纪律,零新素材);
# 禁用=甲案:本族底+α0.45 幽灵化(与 font_disabled_color 同纪律),取代旧「disabled=按压图」错配;
# 焦点=甲案:INK 2px 内缩 1px 环(与 font_focus_color=INK 同源;引擎 focus 样式盒叠加绘制,不遮钮底)
const BUTTON_HOVER_LIFT := Color(1.06, 1.06, 1.06, 1.0) # 悬停提亮档(modulate 乘数>1=中间调提亮,高光像素封顶不漂)
const BUTTON_GHOST_ALPHA := 0.45 # 禁用幽灵化 α(与文字禁用同档)
const BUTTON_FOCUS_RING_W := 2.0 # 焦点环宽 px
const BUTTON_FOCUS_RING_INSET := 1.0 # 焦点环内缩 px(负 expand margin 实现)
static func _focus_ring_sb() -> StyleBoxFlat:
var sb := StyleBoxFlat.new()
sb.bg_color = Color(0, 0, 0, 0)
sb.draw_center = false
sb.set_border_width_all(BUTTON_FOCUS_RING_W)
sb.border_color = INK
sb.set_expand_margin_all(-BUTTON_FOCUS_RING_INSET)
return sb
# 按钮皮(五态统一管线——常态/悬停/按压/禁用/焦点+墨字;缺位素材 = 引擎默认主题)
static func _apply_button_skin(b: Button) -> void:
var tex_n := load_tex(TEX_BUTTON_STONE)
if tex_n == null:
return
var tex_p := load_tex(TEX_BUTTON_STONE_PRESSED)
b.add_theme_stylebox_override("normal", _tex_sb(tex_n, 8.0))
b.add_theme_stylebox_override("hover", _tex_sb(tex_n, 8.0))
b.add_theme_stylebox_override("hover", _tex_sb(tex_n, 8.0, BUTTON_HOVER_LIFT))
if tex_p != null:
b.add_theme_stylebox_override("pressed", _tex_sb(tex_p, 8.0))
b.add_theme_stylebox_override("disabled", _tex_sb(tex_p, 8.0))
b.add_theme_stylebox_override("disabled",
_tex_sb(tex_n, 8.0, Color(1, 1, 1, BUTTON_GHOST_ALPHA)))
b.add_theme_stylebox_override("focus", _focus_ring_sb())
b.add_theme_color_override("font_color", INK)
b.add_theme_color_override("font_hover_color", INK)
b.add_theme_color_override("font_pressed_color", INK)
b.add_theme_color_override("font_focus_color", INK)
b.add_theme_color_override("font_disabled_color", Color(INK, 0.45))
b.add_theme_color_override("font_disabled_color", Color(INK, BUTTON_GHOST_ALPHA))
# ── UI 落码批 M10 公共件(2026-10-04——bcard 竖卡工厂/拨杆开关/样式滑杆/动词按钮/tooltip 主题;
@@ -578,10 +615,14 @@ static func primary_button(text: String, tip := "") -> Button:
b.add_theme_stylebox_override("normal", sb)
b.add_theme_stylebox_override("hover", panel_flat(CANDLE.lightened(0.06), Color8(110, 69, 52),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
b.add_theme_stylebox_override("pressed", panel_flat(Color8(214, 172, 88), Color8(110, 69, 52),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
b.add_theme_stylebox_override("disabled",
panel_flat(Color(CANDLE, BUTTON_GHOST_ALPHA), Color(Color8(110, 69, 52), BUTTON_GHOST_ALPHA), RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
b.add_theme_stylebox_override("focus", _focus_ring_sb())
b.add_theme_color_override("font_color", Color8(58, 42, 8))
b.add_theme_color_override("font_hover_color", Color8(58, 42, 8))
b.add_theme_color_override("font_pressed_color", Color8(58, 42, 8))
b.add_theme_color_override("font_focus_color", Color8(58, 42, 8))
b.add_theme_color_override("font_disabled_color", Color(Color8(58, 42, 8), BUTTON_GHOST_ALPHA))
if tip != "":
b.tooltip_text = tip
return b
@@ -593,9 +634,16 @@ static func danger_button(text: String, tip := "", min_w := 216.0) -> Button:
var sb := panel_flat(Color8(180, 78, 54), Color8(110, 42, 28),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2))
b.add_theme_stylebox_override("normal", sb)
b.add_theme_stylebox_override("hover", panel_flat(Color8(196, 90, 62), Color8(110, 42, 28),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
b.add_theme_stylebox_override("pressed",
panel_flat(Color8(180, 78, 54).darkened(0.18), Color8(110, 42, 28),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
b.add_theme_stylebox_override("disabled",
panel_flat(Color(Color8(180, 78, 54), BUTTON_GHOST_ALPHA), Color(Color8(110, 42, 28), BUTTON_GHOST_ALPHA), RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
b.add_theme_stylebox_override("focus", _focus_ring_sb())
b.add_theme_color_override("font_color", Color8(255, 244, 224))
b.add_theme_color_override("font_hover_color", Color8(255, 244, 224))
b.add_theme_color_override("font_pressed_color", Color8(255, 244, 224))
b.add_theme_color_override("font_focus_color", Color8(255, 244, 224))
b.add_theme_color_override("font_disabled_color", Color(Color8(255, 244, 224), BUTTON_GHOST_ALPHA))
if tip != "":
b.tooltip_text = tip
return b
@@ -657,7 +705,7 @@ class BCard:
box.add_child(mark)
# 图标槽(demo .ic 76×76 槽底+图标 60=20px 源 3 整数倍——P6 甲④ 2× 档升 3× 随 D1 对齐)
var slot := PanelContainer.new()
slot.add_theme_stylebox_override("panel", UiKit.panel_flat(SLOT_BACKDROP, PANEL_BORDER, 0, 1))
slot.add_theme_stylebox_override("panel", UiKit.panel_flat(SLOT_BACKDROP_SOFT, PANEL_BORDER, 0, 1)) # 迭代 26 T5② 明度差降档(柔和档;白图标 ≥3:1 硬约束在役)
slot.custom_minimum_size = Vector2(76, 76)
slot.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
box.add_child(slot)
+1
View File
@@ -34,3 +34,4 @@ theme/custom_font="res://assets/fonts/ui_font_fusion_pixel_zh_hans.ttf"
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"
textures/canvas_textures/default_texture_filter=0
+191
View File
@@ -107,6 +107,11 @@ func _run_core_smoke(db, rng) -> void:
_smoke_playtest_logger()
_smoke_battle_log_view()
_smoke_global_theme()
_smoke_button_five_states()
_smoke_texture_filter()
_smoke_contrast_inks()
_smoke_element_details()
_smoke_diag_button_retired()
_smoke_encounter_roll()
_smoke_attack_chain_fresh_pool()
_smoke_clear_reward_chain()
@@ -5456,6 +5461,192 @@ func _smoke_global_theme() -> void:
tpanel.free(); tlabel.free(); cbx.free(); cbt.free(); pm.free(); ob.free(); lbl.free()
# ---- 按钮五态矩阵冒烟(迭代 26 T1——四工厂产物逐实例 override 断言;V1/V2)----
func _smoke_button_five_states() -> void:
print("--- UI 按钮五态矩阵(四工厂五态样式盒)---")
var uikit_s = load("res://presentation/views/ui_kit.gd")
var states := ["normal", "hover", "pressed", "disabled", "focus"]
# 普通钮(verb_button 同走 button() 打底——同断言覆盖)
var b = uikit_s.button("测试")
for st in states:
check(b.has_theme_stylebox_override(st), "V1 普通钮 %s 样式盒非空" % st)
var n_sb: StyleBox = b.get_theme_stylebox("normal")
var h_sb: StyleBox = b.get_theme_stylebox("hover")
var p_sb: StyleBox = b.get_theme_stylebox("pressed")
var d_sb: StyleBox = b.get_theme_stylebox("disabled")
var f_sb: StyleBox = b.get_theme_stylebox("focus")
check(n_sb is StyleBoxTexture and h_sb is StyleBoxTexture and d_sb is StyleBoxTexture,
"V1 普通钮 normal/hover/disabled 均为石纹九宫格贴图样式盒")
# 悬停=乙案:modulate 提亮(≠常态 WHITE——乙案判据)
var h_mod: Color = (h_sb as StyleBoxTexture).modulate_color
check(h_mod != Color.WHITE and h_mod.r > 1.0, "V1 普通钮 hover=modulate 提亮(乙案,异于常态 WHITE)")
# 禁用=甲案:本族底+α0.45 幽灵化
var d_mod: Color = (d_sb as StyleBoxTexture).modulate_color
check(is_equal_approx(d_mod.a, 0.45) and d_mod.r == 1.0 and d_mod.g == 1.0,
"V1 普通钮 disabled=本族底 α0.45 幽灵化(甲案)")
# 错配修正判据:disabled≠pressed(旧「禁用=按压图」)
check(d_sb != p_sb, "V1 普通钮 disabled≠pressed(旧错配已修正)")
# 焦点=甲案:INK 2px 内缩 1px 环、无底(叠加态不遮钮底)
check(f_sb is StyleBoxFlat, "V1 普通钮 focus 样式盒为扁平环(StyleBoxFlat)")
var fsb := f_sb as StyleBoxFlat
check(fsb.border_color == uikit_s.INK, "V1 焦点环色=INK(与 font_focus_color 同源)")
check(is_equal_approx(fsb.border_width_left, 2.0) and is_equal_approx(fsb.border_width_right, 2.0)
and is_equal_approx(fsb.border_width_top, 2.0) and is_equal_approx(fsb.border_width_bottom, 2.0),
"V1 焦点环宽=2px")
check(is_equal_approx(fsb.expand_margin_left, -1.0) and is_equal_approx(fsb.expand_margin_top, -1.0)
and is_equal_approx(fsb.expand_margin_right, -1.0) and is_equal_approx(fsb.expand_margin_bottom, -1.0),
"V1 焦点环内缩=1px")
check(not fsb.draw_center and fsb.bg_color.a < 0.001, "V1 焦点环无底(叠加态不遮钮底)")
b.free()
# 主钮:五态非空+本族平涂(pressed/disabled 非石纹)+disabled≠pressed+hover 提亮档承现行
var pb = uikit_s.primary_button("测试")
for st in states:
check(pb.has_theme_stylebox_override(st), "V1 主钮 %s 样式盒非空" % st)
var ppn: StyleBox = pb.get_theme_stylebox("pressed")
var pdd: StyleBox = pb.get_theme_stylebox("disabled")
var phv: StyleBox = pb.get_theme_stylebox("hover")
check(ppn is StyleBoxFlat and pdd is StyleBoxFlat, "V1 主钮 pressed/disabled=本族平涂(非石纹)")
check((ppn as StyleBoxFlat).bg_color == Color8(214, 172, 88), "V1 主钮 pressed=烛光黄深档(现行值不动)")
check(is_equal_approx((pdd as StyleBoxFlat).bg_color.a, 0.45), "V1 主钮 disabled=本族底 α0.45 幽灵化")
check(pdd != ppn, "V1 主钮 disabled≠pressed")
check((phv as StyleBoxFlat).bg_color == uikit_s.CANDLE.lightened(0.06), "V1 主钮 hover=提亮档(承现行不动)")
pb.free()
# 危险钮:补本族 pressed(深档)+disabled 幽灵化+focus;pressed/disabled 非石纹
var db = uikit_s.danger_button("测试")
for st in states:
check(db.has_theme_stylebox_override(st), "V1 危险钮 %s 样式盒非空" % st)
var dpr: StyleBox = db.get_theme_stylebox("pressed")
var dds: StyleBox = db.get_theme_stylebox("disabled")
check(dpr is StyleBoxFlat and dds is StyleBoxFlat, "V1 危险钮 pressed/disabled=本族平涂(非石纹,旧错配已修正)")
check((dpr as StyleBoxFlat).bg_color.r < 180.0 / 255.0, "V1 危险钮 pressed=深红深档(暗于常态)")
check(is_equal_approx((dds as StyleBoxFlat).bg_color.a, 0.45), "V1 危险钮 disabled=本族底 α0.45 幽灵化")
check(dds != dpr, "V1 危险钮 disabled≠pressed")
db.free()
# V2 主题红线维持(Button 类型词条零接触+focus 族词条零定义)——与 _smoke_global_theme 同判据复述
var th := ThemeDB.get_project_theme()
if th != null:
check(not th.get_type_list().has("Button"), "V2 红线维持 Button 类型词条零接触")
var focus_hits := ""
for t in th.get_type_list():
for nm in (th.get_stylebox_list(t) + th.get_icon_list(t) + th.get_color_list(t)
+ th.get_font_list(t) + th.get_font_size_list(t) + th.get_constant_list(t)):
if "focus" in String(nm):
focus_hits += "%s/%s " % [t, nm]
check(focus_hits == "", "V2 红线维持 focus 族词条零定义%s" % ("" if focus_hits == "" else "(违例:" + focus_hits + ")"))
# ---- 全局纹理过滤冒烟(迭代 26 T2——工程设置 Nearest+平滑渐变件 LINEAR 白名单;V3)----
func _smoke_texture_filter() -> void:
print("--- UI 全局纹理过滤(Nearest+白名单三件)---")
var v = ProjectSettings.get_setting("rendering/textures/canvas_textures/default_texture_filter")
check(v != null and int(v) == 0, "V3 工程设置 default_texture_filter=0(Nearest,翻转面=立绘/瓦片/九宫格 crisp)")
var uikit_s = load("res://presentation/views/ui_kit.gd")
var vg = uikit_s.vignette_rect()
check(vg.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR, "V3 白名单在位:暗角 vignette 显式 LINEAR")
vg.free()
var bm = uikit_s.beams_rect()
check(bm.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR, "V3 白名单在位:光柱 beams 显式 LINEAR")
# T4 光柱连续 alpha 结构断言(观感归目检):带心峰值 0.05/带缘连续过渡/纵向衰减上亮下暗
var bimg: Image = bm.texture.get_image()
var max_a := 0.0
var mid_a := 0
var top_sum := 0.0
var bot_sum := 0.0
for y in 560:
for x in 560:
var pa := bimg.get_pixel(x, y).a
max_a = maxf(max_a, pa)
if pa > 0.001 and pa < 0.049:
mid_a += 1
if y < 280:
top_sum += pa
else:
bot_sum += pa
check(max_a >= 12.0 / 255.0 and max_a <= 0.05,
"T4 光柱带心峰值 α≈0.05(8bit 截断量化下限 12/255,实测 %.5f)" % max_a)
check(mid_a > 100, "T4 光柱带内连续 alpha 过渡像素 %d 个(旧零过渡硬边已修)" % mid_a)
check(top_sum > bot_sum * 2.0, "T4 光柱纵向衰减生效(上半亮度 %.0f > 下半 %.0f 的 2 倍——上亮下暗)" % [top_sum, bot_sum])
bm.free()
var gb = uikit_s.gradient_base(Color.BLACK, Color.WHITE)
check(gb.texture_filter == CanvasItem.TEXTURE_FILTER_LINEAR, "V3 白名单在位:地图渐变 gradient_base 显式 LINEAR")
gb.free()
# 既有显式 LINEAR 两处不丢(源码锚定——脚影/状态图标)
var bw_src: String = FileAccess.get_file_as_string("res://presentation/views/board_world.gd")
check(bw_src.contains("shadow.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR"),
"V3 脚影显式 LINEAR 不丢(board_world.gd)")
var ss_src: String = FileAccess.get_file_as_string("res://presentation/views/status_slot_row.gd")
check(ss_src.contains("icon.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR"),
"V3 状态图标显式 LINEAR 不丢(status_slot_row.gd)")
# ---- <4.5:1 文字位换墨冒烟(迭代 26 T3——站桩徽标两族亮档+RAP 行深石墨;V4)----
func _smoke_contrast_inks() -> void:
print("--- UI 残余 <4.5:1 文字位换墨(V4 对比度锚定)---")
var uikit_s = load("res://presentation/views/ui_kit.gd")
var stage: Color = uikit_s.STAGE_BG
var r_order := _theme_contrast(uikit_s.GOLD_BRIGHT, stage)
check(r_order >= 4.5, "V4 顺位徽标/重甲徽标 GOLD_BRIGHT 对舞台底 %.2f:1 ≥4.5:1" % r_order)
var r_ward := _theme_contrast(uikit_s.HERO_BLUE_BRIGHT, stage)
check(r_ward >= 4.5, "V4 魔法护甲徽标 HERO_BLUE_BRIGHT 对舞台底 %.2f:1 ≥4.5:1" % r_ward)
var r_rap := _theme_contrast(uikit_s.STONE_DEEP, uikit_s.PANEL_DATA)
check(r_rap >= 4.5, "V4 RAP 反馈行 STONE_DEEP 对数据板 %.2f:1 ≥4.5:1" % r_rap)
# 消费点源码锚定:新墨在位、旧裸用点清零、语义岗常量不动
var sb_src: String = FileAccess.get_file_as_string("res://presentation/views/standee_body.gd")
check(sb_src.contains("HORIZONTAL_ALIGNMENT_LEFT, -1, 13, UiKit.GOLD_BRIGHT"),
"V4 顺位徽标①②③ 墨=GOLD_BRIGHT(13px 行原位)")
check(sb_src.contains("col = UiKit.HERO_BLUE_BRIGHT"), "V4 魔法护甲徽标墨=HERO_BLUE_BRIGHT")
check(sb_src.contains("col = UiKit.GOLD_BRIGHT"), "V4 重甲徽标墨=GOLD_BRIGHT")
check(sb_src.contains("if _intent_col == UiKit.GOLD_DEEP:"),
"V4 GOLD_DEEP 语义岗不动(standee:199 意图动画判据)")
var ap_src: String = FileAccess.get_file_as_string("res://presentation/views/ap_bar_view.gd")
check(ap_src.contains('UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE_DEEP)'),
"V4 RAP 反馈行墨=STONE_DEEP(原位行锚定)")
check(not ap_src.contains("STONE_FAINT"), "V4 RAP 反馈行旧墨 STONE_FAINT 清零(含注记面)")
# ---- 元素细节余②③冒烟(迭代 26 T5——BCard 图标槽明度差降档+商店居中+营地四栏等高)----
func _smoke_element_details() -> void:
print("--- UI 元素细节余②③(图标槽降档+布局修)---")
var uikit_s = load("res://presentation/views/ui_kit.gd")
# ②白图标 ≥3:1 图形对比硬约束(代拍板6)+降档方向锚定
var r_white := _theme_contrast(Color.WHITE, uikit_s.SLOT_BACKDROP_SOFT)
check(r_white >= 3.0, "T5② BCard 图标槽白图标对比 %.2f:1 ≥3:1(硬约束)" % r_white)
var r_new := _theme_contrast(uikit_s.SLOT_BACKDROP_SOFT, uikit_s.PANEL_DATA)
var r_old := _theme_contrast(uikit_s.SLOT_BACKDROP, uikit_s.PANEL_DATA)
check(r_new < r_old, "T5② 明度差降档生效(槽对卡面 %.2f:1 < 旧 %.2f:1)" % [r_new, r_old])
var uk_src: String = FileAccess.get_file_as_string("res://presentation/views/ui_kit.gd")
check(uk_src.contains("UiKit.panel_flat(SLOT_BACKDROP_SOFT, PANEL_BORDER, 0, 1)"),
"T5② BCard 图标槽落柔和档(SLOT_BACKDROP_SOFT)")
var ss_src: String = FileAccess.get_file_as_string("res://presentation/views/status_slot_row.gd")
check(ss_src.contains("UiKit.SLOT_BACKDROP"), "T5② B 区状态图标槽仍用深档(SLOT_BACKDROP,未误伤)")
# ③布局修源码锚定
var shop_src: String = FileAccess.get_file_as_string("res://presentation/views/shop_overlay_view.gd")
check(shop_src.contains("_cards_box.alignment = BoxContainer.ALIGNMENT_CENTER"),
"T5③ 商店卡片区垂直居中(ALIGNMENT_CENTER)")
var camp_src: String = FileAccess.get_file_as_string("res://presentation/views/camp_overlay_view.gd")
var camp_min := camp_src.count("custom_minimum_size = Vector2(0, 336)")
check(camp_min == 3, "T5③ 营地三 holder 统一 min 高 336(实测 %d 处)" % camp_min)
var camp_fill := camp_src.count("size_flags_vertical = Control.SIZE_FILL")
check(camp_fill == 4, "T5③ 营地四栏面板纵向 FILL 等高(实测 %d 处)" % camp_fill)
check(camp_src.count("SIZE_SHRINK_CENTER") >= 3, "T5③ 三处锁文垂直居中在位")
# ---- 诊断转储钮退役冒烟(迭代 26 T5b——角落钮退出玩家画面+转储入口迁调试浮窗;V6)----
func _smoke_diag_button_retired() -> void:
print("--- UI 诊断转储钮退役(V6:玩家画面清零+浮窗可达)---")
var main_src: String = FileAccess.get_file_as_string("res://presentation/main.gd")
check(not main_src.contains("build_diag_button"), "V6 主区无 build_diag_button 调用残留(旧实现同批清理)")
check(main_src.contains("debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)"),
"V6 转储入口连接随迁(组合根接浮窗 diagnose_requested 信号)")
check(main_src.contains("func _on_diagnose_pressed()"), "V6 转储编排承接函数在位(dump_diagnosis 卡死取证保留)")
var asm_src: String = FileAccess.get_file_as_string("res://presentation/ui_assembler.gd")
check(not asm_src.contains("build_diag_button"), "V6 build_diag_button 工厂已退役(ui_assembler 零残留)")
var dbg_src: String = FileAccess.get_file_as_string("res://presentation/views/debug_overlay.gd")
check(dbg_src.contains("signal diagnose_requested"), "V6 调试浮窗 diagnose_requested 信号在位")
check(dbg_src.contains("_emit_diagnose_requested"), "V6 浮窗转储钮外发方法在位(纯视图边界)")
check(dbg_src.contains('UiKit.button("转储")'), "V6 浮窗诊断转储钮在位(玩家画面角落钮清零后唯一入口)")
# ---- 存档序列化冒烟(SaveManager——读写往返/新档合法/余额拒花/锻造出域拒/高版本拒读禁写/
# 损坏兜底/迁移链/域界干净/摘要 cap/摘要集成/生涯终身计数域 v6)。测试文件统一 user://smoke_save_*.json 隔离,
# 段首清场+段尾清理——绝不碰 user://save.json(红线)。
+6 -5
View File
@@ -23,10 +23,11 @@ const _OVERLAY_READONLY_SIGNALS: Array[String] = [
"text_changed", "text_submitted", "value_changed", "item_selected",
"gui_input", "focus_entered", "focus_exited", "closed",
]
# 视图内白名单方法(fx 切换/应用/重置/模拟/两层调试/关闭/滑条联动——全部视图自身信号外发前置,
# 视图内白名单方法(fx 切换/应用/重置/模拟/两层调试/诊断转储/关闭/滑条联动——全部视图自身信号外发前置,
# 零跨对象写;清单外任何新连接一律红须入册)
const _OVERLAY_VIEW_METHODS: Array[String] = ["_on_fx_toggle", "_apply_btn_pressed", "_reset_btn_pressed",
"_emit_simulate_requested", "_emit_debug_levels", "_emit_closed", "_on_slider_value_changed"]
"_emit_simulate_requested", "_emit_debug_levels", "_emit_diagnose_requested", "_emit_closed",
"_on_slider_value_changed"]
func _ready() -> void:
@@ -194,7 +195,7 @@ func _ready() -> void:
# 只读巡检(18卡⑦ T3——17卡③「白名单外零连接」形制沿用):浮窗子树全控件 × 游戏语义信号名清单
# 全连接巡检——白名单={视图自身七方法 + 组合根 _on_modal_closed(closed 外发消费)};
# 全连接巡检——白名单={视图自身八方法 + 组合根 _on_modal_closed(closed 外发消费)};
# 当局数据区块=纯标签零连接在此被一并约束(新增任何连接=红须入册审查)。
func _overlay_readonly_contract(main: Node, problems: Array) -> void:
var overlay: Control = main.debug_overlay
@@ -216,8 +217,8 @@ func _overlay_readonly_contract(main: Node, problems: Array) -> void:
% [n.name, sig_name, host, m])
for ch in n.get_children():
stack.append(ch)
if conn_count != 10:
problems.append("[readonly] 信号连接数=%d 应=10(fx切换/应用/重置/模拟/两层调试/关闭六钮+滑条2+closed 外发+引擎滚动条 drag 1——多连接=写通路嫌疑)" % conn_count)
if conn_count != 11:
problems.append("[readonly] 信号连接数=%d 应=11(fx切换/应用/重置/模拟/两层调试/诊断转储/关闭七钮+滑条2+closed 外发+引擎滚动条 drag 1——多连接=写通路嫌疑)" % conn_count)
# 零动效契约(17卡③ R5 形制——子树零 AnimationPlayer + 视图源码零 tween/动效包装调用)